aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2017-12-02 15:44:05 -0500
committerDavid S. Miller <davem@davemloft.net>2017-12-04 11:32:22 -0500
commitaddf9b90de22f7aaad0db39bccb5d51ac47dd4e1 (patch)
treec503ab68da10306eae7170efb19dd614ba9cc93e
parent9753c21f55d4ca2b02bbe81448f2a552ce79d068 (diff)
net: rtnetlink: use rcu to free rtnl message handlers
rtnetlink is littered with READ_ONCE() because we can have read accesses while another cpu can write to the structure we're reading by (un)registering doit or dumpit handlers. This patch changes this so that (un)registering cpu allocates a new structure and then publishes it via rcu_assign_pointer, i.e. once another cpu can see such pointer no modifications will occur anymore. based on initial patch from Peter Zijlstra. Cc: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/core/rtnetlink.c154
1 files changed, 101 insertions, 53 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index dabba2a91fc8..ff292d3f2c41 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -63,6 +63,7 @@ struct rtnl_link {
63 rtnl_doit_func doit; 63 rtnl_doit_func doit;
64 rtnl_dumpit_func dumpit; 64 rtnl_dumpit_func dumpit;
65 unsigned int flags; 65 unsigned int flags;
66 struct rcu_head rcu;
66}; 67};
67 68
68static DEFINE_MUTEX(rtnl_mutex); 69static DEFINE_MUTEX(rtnl_mutex);
@@ -127,7 +128,7 @@ bool lockdep_rtnl_is_held(void)
127EXPORT_SYMBOL(lockdep_rtnl_is_held); 128EXPORT_SYMBOL(lockdep_rtnl_is_held);
128#endif /* #ifdef CONFIG_PROVE_LOCKING */ 129#endif /* #ifdef CONFIG_PROVE_LOCKING */
129 130
130static struct rtnl_link __rcu *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; 131static struct rtnl_link __rcu **rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];
131static refcount_t rtnl_msg_handlers_ref[RTNL_FAMILY_MAX + 1]; 132static refcount_t rtnl_msg_handlers_ref[RTNL_FAMILY_MAX + 1];
132 133
133static inline int rtm_msgindex(int msgtype) 134static inline int rtm_msgindex(int msgtype)
@@ -144,6 +145,20 @@ static inline int rtm_msgindex(int msgtype)
144 return msgindex; 145 return msgindex;
145} 146}
146 147
148static struct rtnl_link *rtnl_get_link(int protocol, int msgtype)
149{
150 struct rtnl_link **tab;
151
152 if (protocol >= ARRAY_SIZE(rtnl_msg_handlers))
153 protocol = PF_UNSPEC;
154
155 tab = rcu_dereference_rtnl(rtnl_msg_handlers[protocol]);
156 if (!tab)
157 tab = rcu_dereference_rtnl(rtnl_msg_handlers[PF_UNSPEC]);
158
159 return tab[msgtype];
160}
161
147/** 162/**
148 * __rtnl_register - Register a rtnetlink message type 163 * __rtnl_register - Register a rtnetlink message type
149 * @protocol: Protocol family or PF_UNSPEC 164 * @protocol: Protocol family or PF_UNSPEC
@@ -166,28 +181,52 @@ int __rtnl_register(int protocol, int msgtype,
166 rtnl_doit_func doit, rtnl_dumpit_func dumpit, 181 rtnl_doit_func doit, rtnl_dumpit_func dumpit,
167 unsigned int flags) 182 unsigned int flags)
168{ 183{
169 struct rtnl_link *tab; 184 struct rtnl_link **tab, *link, *old;
170 int msgindex; 185 int msgindex;
186 int ret = -ENOBUFS;
171 187
172 BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); 188 BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
173 msgindex = rtm_msgindex(msgtype); 189 msgindex = rtm_msgindex(msgtype);
174 190
175 tab = rcu_dereference_raw(rtnl_msg_handlers[protocol]); 191 rtnl_lock();
192 tab = rtnl_msg_handlers[protocol];
176 if (tab == NULL) { 193 if (tab == NULL) {
177 tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); 194 tab = kcalloc(RTM_NR_MSGTYPES, sizeof(void *), GFP_KERNEL);
178 if (tab == NULL) 195 if (!tab)
179 return -ENOBUFS; 196 goto unlock;
180 197
198 /* ensures we see the 0 stores */
181 rcu_assign_pointer(rtnl_msg_handlers[protocol], tab); 199 rcu_assign_pointer(rtnl_msg_handlers[protocol], tab);
182 } 200 }
183 201
202 old = rtnl_dereference(tab[msgindex]);
203 if (old) {
204 link = kmemdup(old, sizeof(*old), GFP_KERNEL);
205 if (!link)
206 goto unlock;
207 } else {
208 link = kzalloc(sizeof(*link), GFP_KERNEL);
209 if (!link)
210 goto unlock;
211 }
212
213 WARN_ON(doit && link->doit && link->doit != doit);
184 if (doit) 214 if (doit)
185 tab[msgindex].doit = doit; 215 link->doit = doit;
216 WARN_ON(dumpit && link->dumpit && link->dumpit != dumpit);
186 if (dumpit) 217 if (dumpit)
187 tab[msgindex].dumpit = dumpit; 218 link->dumpit = dumpit;
188 tab[msgindex].flags |= flags;
189 219
190 return 0; 220 link->flags |= flags;
221
222 /* publish protocol:msgtype */
223 rcu_assign_pointer(tab[msgindex], link);
224 ret = 0;
225 if (old)
226 kfree_rcu(old, rcu);
227unlock:
228 rtnl_unlock();
229 return ret;
191} 230}
192EXPORT_SYMBOL_GPL(__rtnl_register); 231EXPORT_SYMBOL_GPL(__rtnl_register);
193 232
@@ -220,24 +259,25 @@ EXPORT_SYMBOL_GPL(rtnl_register);
220 */ 259 */
221int rtnl_unregister(int protocol, int msgtype) 260int rtnl_unregister(int protocol, int msgtype)
222{ 261{
223 struct rtnl_link *handlers; 262 struct rtnl_link **tab, *link;
224 int msgindex; 263 int msgindex;
225 264
226 BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); 265 BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
227 msgindex = rtm_msgindex(msgtype); 266 msgindex = rtm_msgindex(msgtype);
228 267
229 rtnl_lock(); 268 rtnl_lock();
230 handlers = rtnl_dereference(rtnl_msg_handlers[protocol]); 269 tab = rtnl_dereference(rtnl_msg_handlers[protocol]);
231 if (!handlers) { 270 if (!tab) {
232 rtnl_unlock(); 271 rtnl_unlock();
233 return -ENOENT; 272 return -ENOENT;
234 } 273 }
235 274
236 handlers[msgindex].doit = NULL; 275 link = tab[msgindex];
237 handlers[msgindex].dumpit = NULL; 276 rcu_assign_pointer(tab[msgindex], NULL);
238 handlers[msgindex].flags = 0;
239 rtnl_unlock(); 277 rtnl_unlock();
240 278
279 kfree_rcu(link, rcu);
280
241 return 0; 281 return 0;
242} 282}
243EXPORT_SYMBOL_GPL(rtnl_unregister); 283EXPORT_SYMBOL_GPL(rtnl_unregister);
@@ -251,20 +291,29 @@ EXPORT_SYMBOL_GPL(rtnl_unregister);
251 */ 291 */
252void rtnl_unregister_all(int protocol) 292void rtnl_unregister_all(int protocol)
253{ 293{
254 struct rtnl_link *handlers; 294 struct rtnl_link **tab, *link;
295 int msgindex;
255 296
256 BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); 297 BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
257 298
258 rtnl_lock(); 299 rtnl_lock();
259 handlers = rtnl_dereference(rtnl_msg_handlers[protocol]); 300 tab = rtnl_msg_handlers[protocol];
260 RCU_INIT_POINTER(rtnl_msg_handlers[protocol], NULL); 301 RCU_INIT_POINTER(rtnl_msg_handlers[protocol], NULL);
302 for (msgindex = 0; msgindex < RTM_NR_MSGTYPES; msgindex++) {
303 link = tab[msgindex];
304 if (!link)
305 continue;
306
307 rcu_assign_pointer(tab[msgindex], NULL);
308 kfree_rcu(link, rcu);
309 }
261 rtnl_unlock(); 310 rtnl_unlock();
262 311
263 synchronize_net(); 312 synchronize_net();
264 313
265 while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1) 314 while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1)
266 schedule(); 315 schedule();
267 kfree(handlers); 316 kfree(tab);
268} 317}
269EXPORT_SYMBOL_GPL(rtnl_unregister_all); 318EXPORT_SYMBOL_GPL(rtnl_unregister_all);
270 319
@@ -2973,18 +3022,26 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
2973 s_idx = 1; 3022 s_idx = 1;
2974 3023
2975 for (idx = 1; idx <= RTNL_FAMILY_MAX; idx++) { 3024 for (idx = 1; idx <= RTNL_FAMILY_MAX; idx++) {
3025 struct rtnl_link **tab;
2976 int type = cb->nlh->nlmsg_type-RTM_BASE; 3026 int type = cb->nlh->nlmsg_type-RTM_BASE;
2977 struct rtnl_link *handlers; 3027 struct rtnl_link *link;
2978 rtnl_dumpit_func dumpit; 3028 rtnl_dumpit_func dumpit;
2979 3029
2980 if (idx < s_idx || idx == PF_PACKET) 3030 if (idx < s_idx || idx == PF_PACKET)
2981 continue; 3031 continue;
2982 3032
2983 handlers = rtnl_dereference(rtnl_msg_handlers[idx]); 3033 if (type < 0 || type >= RTM_NR_MSGTYPES)
2984 if (!handlers)
2985 continue; 3034 continue;
2986 3035
2987 dumpit = READ_ONCE(handlers[type].dumpit); 3036 tab = rcu_dereference_rtnl(rtnl_msg_handlers[idx]);
3037 if (!tab)
3038 continue;
3039
3040 link = tab[type];
3041 if (!link)
3042 continue;
3043
3044 dumpit = link->dumpit;
2988 if (!dumpit) 3045 if (!dumpit)
2989 continue; 3046 continue;
2990 3047
@@ -4314,7 +4371,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4314 struct netlink_ext_ack *extack) 4371 struct netlink_ext_ack *extack)
4315{ 4372{
4316 struct net *net = sock_net(skb->sk); 4373 struct net *net = sock_net(skb->sk);
4317 struct rtnl_link *handlers; 4374 struct rtnl_link *link;
4318 int err = -EOPNOTSUPP; 4375 int err = -EOPNOTSUPP;
4319 rtnl_doit_func doit; 4376 rtnl_doit_func doit;
4320 unsigned int flags; 4377 unsigned int flags;
@@ -4338,32 +4395,20 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4338 if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) 4395 if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN))
4339 return -EPERM; 4396 return -EPERM;
4340 4397
4341 if (family >= ARRAY_SIZE(rtnl_msg_handlers))
4342 family = PF_UNSPEC;
4343
4344 rcu_read_lock(); 4398 rcu_read_lock();
4345 handlers = rcu_dereference(rtnl_msg_handlers[family]);
4346 if (!handlers) {
4347 family = PF_UNSPEC;
4348 handlers = rcu_dereference(rtnl_msg_handlers[family]);
4349 }
4350
4351 if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { 4399 if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
4352 struct sock *rtnl; 4400 struct sock *rtnl;
4353 rtnl_dumpit_func dumpit; 4401 rtnl_dumpit_func dumpit;
4354 u16 min_dump_alloc = 0; 4402 u16 min_dump_alloc = 0;
4355 4403
4356 dumpit = READ_ONCE(handlers[type].dumpit); 4404 link = rtnl_get_link(family, type);
4357 if (!dumpit) { 4405 if (!link || !link->dumpit) {
4358 family = PF_UNSPEC; 4406 family = PF_UNSPEC;
4359 handlers = rcu_dereference(rtnl_msg_handlers[PF_UNSPEC]); 4407 link = rtnl_get_link(family, type);
4360 if (!handlers) 4408 if (!link || !link->dumpit)
4361 goto err_unlock;
4362
4363 dumpit = READ_ONCE(handlers[type].dumpit);
4364 if (!dumpit)
4365 goto err_unlock; 4409 goto err_unlock;
4366 } 4410 }
4411 dumpit = link->dumpit;
4367 4412
4368 refcount_inc(&rtnl_msg_handlers_ref[family]); 4413 refcount_inc(&rtnl_msg_handlers_ref[family]);
4369 4414
@@ -4384,33 +4429,36 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4384 return err; 4429 return err;
4385 } 4430 }
4386 4431
4387 doit = READ_ONCE(handlers[type].doit); 4432 link = rtnl_get_link(family, type);
4388 if (!doit) { 4433 if (!link || !link->doit) {
4389 family = PF_UNSPEC; 4434 family = PF_UNSPEC;
4390 handlers = rcu_dereference(rtnl_msg_handlers[family]); 4435 link = rtnl_get_link(PF_UNSPEC, type);
4436 if (!link || !link->doit)
4437 goto out_unlock;
4391 } 4438 }
4392 4439
4393 flags = READ_ONCE(handlers[type].flags); 4440 flags = link->flags;
4394 if (flags & RTNL_FLAG_DOIT_UNLOCKED) { 4441 if (flags & RTNL_FLAG_DOIT_UNLOCKED) {
4395 refcount_inc(&rtnl_msg_handlers_ref[family]); 4442 refcount_inc(&rtnl_msg_handlers_ref[family]);
4396 doit = READ_ONCE(handlers[type].doit); 4443 doit = link->doit;
4397 rcu_read_unlock(); 4444 rcu_read_unlock();
4398 if (doit) 4445 if (doit)
4399 err = doit(skb, nlh, extack); 4446 err = doit(skb, nlh, extack);
4400 refcount_dec(&rtnl_msg_handlers_ref[family]); 4447 refcount_dec(&rtnl_msg_handlers_ref[family]);
4401 return err; 4448 return err;
4402 } 4449 }
4403
4404 rcu_read_unlock(); 4450 rcu_read_unlock();
4405 4451
4406 rtnl_lock(); 4452 rtnl_lock();
4407 handlers = rtnl_dereference(rtnl_msg_handlers[family]); 4453 link = rtnl_get_link(family, type);
4408 if (handlers) { 4454 if (link && link->doit)
4409 doit = READ_ONCE(handlers[type].doit); 4455 err = link->doit(skb, nlh, extack);
4410 if (doit)
4411 err = doit(skb, nlh, extack);
4412 }
4413 rtnl_unlock(); 4456 rtnl_unlock();
4457
4458 return err;
4459
4460out_unlock:
4461 rcu_read_unlock();
4414 return err; 4462 return err;
4415 4463
4416err_unlock: 4464err_unlock: