diff options
author | Florian Westphal <fw@strlen.de> | 2017-12-02 15:44:06 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-12-04 11:32:31 -0500 |
commit | e4202511480da5f8e6870d8f6ecbb821aeaa8caf (patch) | |
tree | e5fadf5d76149f543fc003154f844b2ca19579bd | |
parent | addf9b90de22f7aaad0db39bccb5d51ac47dd4e1 (diff) |
rtnetlink: get reference on module before invoking handlers
Add yet another rtnl_register function. It will be used by modules
that can be removed.
The passed module struct is used to prevent module unload while
a netlink dump is in progress or when a DOIT_UNLOCKED doit callback
is called.
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-- | include/net/rtnetlink.h | 2 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 113 |
2 files changed, 80 insertions, 35 deletions
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index ead018744ff5..e326b3f9eb5f 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h | |||
@@ -17,6 +17,8 @@ int __rtnl_register(int protocol, int msgtype, | |||
17 | rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); | 17 | rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); |
18 | void rtnl_register(int protocol, int msgtype, | 18 | void rtnl_register(int protocol, int msgtype, |
19 | rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); | 19 | rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); |
20 | int rtnl_register_module(struct module *owner, int protocol, int msgtype, | ||
21 | rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); | ||
20 | int rtnl_unregister(int protocol, int msgtype); | 22 | int rtnl_unregister(int protocol, int msgtype); |
21 | void rtnl_unregister_all(int protocol); | 23 | void rtnl_unregister_all(int protocol); |
22 | 24 | ||
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index ff292d3f2c41..de6390365c90 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -62,6 +62,7 @@ | |||
62 | struct rtnl_link { | 62 | struct rtnl_link { |
63 | rtnl_doit_func doit; | 63 | rtnl_doit_func doit; |
64 | rtnl_dumpit_func dumpit; | 64 | rtnl_dumpit_func dumpit; |
65 | struct module *owner; | ||
65 | unsigned int flags; | 66 | unsigned int flags; |
66 | struct rcu_head rcu; | 67 | struct rcu_head rcu; |
67 | }; | 68 | }; |
@@ -129,7 +130,6 @@ EXPORT_SYMBOL(lockdep_rtnl_is_held); | |||
129 | #endif /* #ifdef CONFIG_PROVE_LOCKING */ | 130 | #endif /* #ifdef CONFIG_PROVE_LOCKING */ |
130 | 131 | ||
131 | static struct rtnl_link __rcu **rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; | 132 | static struct rtnl_link __rcu **rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; |
132 | static refcount_t rtnl_msg_handlers_ref[RTNL_FAMILY_MAX + 1]; | ||
133 | 133 | ||
134 | static inline int rtm_msgindex(int msgtype) | 134 | static inline int rtm_msgindex(int msgtype) |
135 | { | 135 | { |
@@ -159,27 +159,10 @@ static struct rtnl_link *rtnl_get_link(int protocol, int msgtype) | |||
159 | return tab[msgtype]; | 159 | return tab[msgtype]; |
160 | } | 160 | } |
161 | 161 | ||
162 | /** | 162 | static int rtnl_register_internal(struct module *owner, |
163 | * __rtnl_register - Register a rtnetlink message type | 163 | int protocol, int msgtype, |
164 | * @protocol: Protocol family or PF_UNSPEC | 164 | rtnl_doit_func doit, rtnl_dumpit_func dumpit, |
165 | * @msgtype: rtnetlink message type | 165 | unsigned int flags) |
166 | * @doit: Function pointer called for each request message | ||
167 | * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message | ||
168 | * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions | ||
169 | * | ||
170 | * Registers the specified function pointers (at least one of them has | ||
171 | * to be non-NULL) to be called whenever a request message for the | ||
172 | * specified protocol family and message type is received. | ||
173 | * | ||
174 | * The special protocol family PF_UNSPEC may be used to define fallback | ||
175 | * function pointers for the case when no entry for the specific protocol | ||
176 | * family exists. | ||
177 | * | ||
178 | * Returns 0 on success or a negative error code. | ||
179 | */ | ||
180 | int __rtnl_register(int protocol, int msgtype, | ||
181 | rtnl_doit_func doit, rtnl_dumpit_func dumpit, | ||
182 | unsigned int flags) | ||
183 | { | 166 | { |
184 | struct rtnl_link **tab, *link, *old; | 167 | struct rtnl_link **tab, *link, *old; |
185 | int msgindex; | 168 | int msgindex; |
@@ -210,6 +193,9 @@ int __rtnl_register(int protocol, int msgtype, | |||
210 | goto unlock; | 193 | goto unlock; |
211 | } | 194 | } |
212 | 195 | ||
196 | WARN_ON(link->owner && link->owner != owner); | ||
197 | link->owner = owner; | ||
198 | |||
213 | WARN_ON(doit && link->doit && link->doit != doit); | 199 | WARN_ON(doit && link->doit && link->doit != doit); |
214 | if (doit) | 200 | if (doit) |
215 | link->doit = doit; | 201 | link->doit = doit; |
@@ -228,6 +214,54 @@ unlock: | |||
228 | rtnl_unlock(); | 214 | rtnl_unlock(); |
229 | return ret; | 215 | return ret; |
230 | } | 216 | } |
217 | |||
218 | /** | ||
219 | * rtnl_register_module - Register a rtnetlink message type | ||
220 | * | ||
221 | * @owner: module registering the hook (THIS_MODULE) | ||
222 | * @protocol: Protocol family or PF_UNSPEC | ||
223 | * @msgtype: rtnetlink message type | ||
224 | * @doit: Function pointer called for each request message | ||
225 | * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message | ||
226 | * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions | ||
227 | * | ||
228 | * Like rtnl_register, but for use by removable modules. | ||
229 | */ | ||
230 | int rtnl_register_module(struct module *owner, | ||
231 | int protocol, int msgtype, | ||
232 | rtnl_doit_func doit, rtnl_dumpit_func dumpit, | ||
233 | unsigned int flags) | ||
234 | { | ||
235 | return rtnl_register_internal(owner, protocol, msgtype, | ||
236 | doit, dumpit, flags); | ||
237 | } | ||
238 | EXPORT_SYMBOL_GPL(rtnl_register_module); | ||
239 | |||
240 | /** | ||
241 | * __rtnl_register - Register a rtnetlink message type | ||
242 | * @protocol: Protocol family or PF_UNSPEC | ||
243 | * @msgtype: rtnetlink message type | ||
244 | * @doit: Function pointer called for each request message | ||
245 | * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message | ||
246 | * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions | ||
247 | * | ||
248 | * Registers the specified function pointers (at least one of them has | ||
249 | * to be non-NULL) to be called whenever a request message for the | ||
250 | * specified protocol family and message type is received. | ||
251 | * | ||
252 | * The special protocol family PF_UNSPEC may be used to define fallback | ||
253 | * function pointers for the case when no entry for the specific protocol | ||
254 | * family exists. | ||
255 | * | ||
256 | * Returns 0 on success or a negative error code. | ||
257 | */ | ||
258 | int __rtnl_register(int protocol, int msgtype, | ||
259 | rtnl_doit_func doit, rtnl_dumpit_func dumpit, | ||
260 | unsigned int flags) | ||
261 | { | ||
262 | return rtnl_register_internal(NULL, protocol, msgtype, | ||
263 | doit, dumpit, flags); | ||
264 | } | ||
231 | EXPORT_SYMBOL_GPL(__rtnl_register); | 265 | EXPORT_SYMBOL_GPL(__rtnl_register); |
232 | 266 | ||
233 | /** | 267 | /** |
@@ -311,8 +345,6 @@ void rtnl_unregister_all(int protocol) | |||
311 | 345 | ||
312 | synchronize_net(); | 346 | synchronize_net(); |
313 | 347 | ||
314 | while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1) | ||
315 | schedule(); | ||
316 | kfree(tab); | 348 | kfree(tab); |
317 | } | 349 | } |
318 | EXPORT_SYMBOL_GPL(rtnl_unregister_all); | 350 | EXPORT_SYMBOL_GPL(rtnl_unregister_all); |
@@ -4372,6 +4404,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4372 | { | 4404 | { |
4373 | struct net *net = sock_net(skb->sk); | 4405 | struct net *net = sock_net(skb->sk); |
4374 | struct rtnl_link *link; | 4406 | struct rtnl_link *link; |
4407 | struct module *owner; | ||
4375 | int err = -EOPNOTSUPP; | 4408 | int err = -EOPNOTSUPP; |
4376 | rtnl_doit_func doit; | 4409 | rtnl_doit_func doit; |
4377 | unsigned int flags; | 4410 | unsigned int flags; |
@@ -4408,24 +4441,32 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4408 | if (!link || !link->dumpit) | 4441 | if (!link || !link->dumpit) |
4409 | goto err_unlock; | 4442 | goto err_unlock; |
4410 | } | 4443 | } |
4444 | owner = link->owner; | ||
4411 | dumpit = link->dumpit; | 4445 | dumpit = link->dumpit; |
4412 | 4446 | ||
4413 | refcount_inc(&rtnl_msg_handlers_ref[family]); | ||
4414 | |||
4415 | if (type == RTM_GETLINK - RTM_BASE) | 4447 | if (type == RTM_GETLINK - RTM_BASE) |
4416 | min_dump_alloc = rtnl_calcit(skb, nlh); | 4448 | min_dump_alloc = rtnl_calcit(skb, nlh); |
4417 | 4449 | ||
4450 | err = 0; | ||
4451 | /* need to do this before rcu_read_unlock() */ | ||
4452 | if (!try_module_get(owner)) | ||
4453 | err = -EPROTONOSUPPORT; | ||
4454 | |||
4418 | rcu_read_unlock(); | 4455 | rcu_read_unlock(); |
4419 | 4456 | ||
4420 | rtnl = net->rtnl; | 4457 | rtnl = net->rtnl; |
4421 | { | 4458 | if (err == 0) { |
4422 | struct netlink_dump_control c = { | 4459 | struct netlink_dump_control c = { |
4423 | .dump = dumpit, | 4460 | .dump = dumpit, |
4424 | .min_dump_alloc = min_dump_alloc, | 4461 | .min_dump_alloc = min_dump_alloc, |
4462 | .module = owner, | ||
4425 | }; | 4463 | }; |
4426 | err = netlink_dump_start(rtnl, skb, nlh, &c); | 4464 | err = netlink_dump_start(rtnl, skb, nlh, &c); |
4465 | /* netlink_dump_start() will keep a reference on | ||
4466 | * module if dump is still in progress. | ||
4467 | */ | ||
4468 | module_put(owner); | ||
4427 | } | 4469 | } |
4428 | refcount_dec(&rtnl_msg_handlers_ref[family]); | ||
4429 | return err; | 4470 | return err; |
4430 | } | 4471 | } |
4431 | 4472 | ||
@@ -4437,14 +4478,19 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4437 | goto out_unlock; | 4478 | goto out_unlock; |
4438 | } | 4479 | } |
4439 | 4480 | ||
4481 | owner = link->owner; | ||
4482 | if (!try_module_get(owner)) { | ||
4483 | err = -EPROTONOSUPPORT; | ||
4484 | goto out_unlock; | ||
4485 | } | ||
4486 | |||
4440 | flags = link->flags; | 4487 | flags = link->flags; |
4441 | if (flags & RTNL_FLAG_DOIT_UNLOCKED) { | 4488 | if (flags & RTNL_FLAG_DOIT_UNLOCKED) { |
4442 | refcount_inc(&rtnl_msg_handlers_ref[family]); | ||
4443 | doit = link->doit; | 4489 | doit = link->doit; |
4444 | rcu_read_unlock(); | 4490 | rcu_read_unlock(); |
4445 | if (doit) | 4491 | if (doit) |
4446 | err = doit(skb, nlh, extack); | 4492 | err = doit(skb, nlh, extack); |
4447 | refcount_dec(&rtnl_msg_handlers_ref[family]); | 4493 | module_put(owner); |
4448 | return err; | 4494 | return err; |
4449 | } | 4495 | } |
4450 | rcu_read_unlock(); | 4496 | rcu_read_unlock(); |
@@ -4455,6 +4501,8 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4455 | err = link->doit(skb, nlh, extack); | 4501 | err = link->doit(skb, nlh, extack); |
4456 | rtnl_unlock(); | 4502 | rtnl_unlock(); |
4457 | 4503 | ||
4504 | module_put(owner); | ||
4505 | |||
4458 | return err; | 4506 | return err; |
4459 | 4507 | ||
4460 | out_unlock: | 4508 | out_unlock: |
@@ -4546,11 +4594,6 @@ static struct pernet_operations rtnetlink_net_ops = { | |||
4546 | 4594 | ||
4547 | void __init rtnetlink_init(void) | 4595 | void __init rtnetlink_init(void) |
4548 | { | 4596 | { |
4549 | int i; | ||
4550 | |||
4551 | for (i = 0; i < ARRAY_SIZE(rtnl_msg_handlers_ref); i++) | ||
4552 | refcount_set(&rtnl_msg_handlers_ref[i], 1); | ||
4553 | |||
4554 | if (register_pernet_subsys(&rtnetlink_net_ops)) | 4597 | if (register_pernet_subsys(&rtnetlink_net_ops)) |
4555 | panic("rtnetlink_init: cannot initialize rtnetlink\n"); | 4598 | panic("rtnetlink_init: cannot initialize rtnetlink\n"); |
4556 | 4599 | ||