Commit f4d4c49b authored by David S. Miller's avatar David S. Miller

Merge branch 'rtnetlink-rework-handler-registration'

Florian Westphal says:

====================
rtnetlink: rework handler (un)registering

Peter Zijlstra reported (referring to commit 019a3169,
"rtnetlink: add reference counting to prevent module unload while dump is in progress"):

 1) it not in fact a refcount, so using refcount_t is silly
 2) there is a distinct lack of memory barriers, so we can easily
    observe the decrement while the msg_handler is still in progress.
 3) waiting with a schedule()/yield() loop is complete crap and subject
    life-locks, imagine doing that rtnl_unregister_all() from a RT task.

In ancient times rtnetlink exposed a statically-sized table with
preset doit/dumpit handlers to be called for a protocol/type pair.

Later the rtnl_register interface was added and the table was allocated
on demand.  Eventually these were also used by modules.

Problem is that nothing prevents module unload while a netlink dump
is in progress.  netlink dumps can be span multiple recv calls and
netlink core saves the to-be-repeated dumper address for later invocation.

To prevent rmmod the netlink core expects callers to pass in the owning
module so a reference can be taken.

So far rtnetlink wasn't doing this, add new interface to pass THIS_MODULE.
Moreover, when converting parts of the rtnetlink handling to rcu this code
gained way too many READ_ONCE spots, remove them and the extra refcounting.

Take a module reference when running dumpit and doit callbacks
and never alter content of rtnl_link structures after they have been
published via rcu_assign_pointer.

Based partially on earlier patch from Peter.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9753c21f 16feebcf
...@@ -13,10 +13,10 @@ enum rtnl_link_flags { ...@@ -13,10 +13,10 @@ enum rtnl_link_flags {
RTNL_FLAG_DOIT_UNLOCKED = 1, RTNL_FLAG_DOIT_UNLOCKED = 1,
}; };
int __rtnl_register(int protocol, int msgtype,
rtnl_doit_func, rtnl_dumpit_func, unsigned int flags);
void rtnl_register(int protocol, int msgtype, void rtnl_register(int protocol, int msgtype,
rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); rtnl_doit_func, rtnl_dumpit_func, unsigned int flags);
int rtnl_register_module(struct module *owner, int protocol, int msgtype,
rtnl_doit_func, rtnl_dumpit_func, unsigned int flags);
int rtnl_unregister(int protocol, int msgtype); int rtnl_unregister(int protocol, int msgtype);
void rtnl_unregister_all(int protocol); void rtnl_unregister_all(int protocol);
......
...@@ -760,9 +760,9 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -760,9 +760,9 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
void br_mdb_init(void) void br_mdb_init(void)
{ {
rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, 0); rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, 0);
rtnl_register(PF_BRIDGE, RTM_NEWMDB, br_mdb_add, NULL, 0); rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_NEWMDB, br_mdb_add, NULL, 0);
rtnl_register(PF_BRIDGE, RTM_DELMDB, br_mdb_del, NULL, 0); rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_DELMDB, br_mdb_del, NULL, 0);
} }
void br_mdb_uninit(void) void br_mdb_uninit(void)
......
...@@ -1014,6 +1014,8 @@ static struct pernet_operations cangw_pernet_ops = { ...@@ -1014,6 +1014,8 @@ static struct pernet_operations cangw_pernet_ops = {
static __init int cgw_module_init(void) static __init int cgw_module_init(void)
{ {
int ret;
/* sanitize given module parameter */ /* sanitize given module parameter */
max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS); max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS);
...@@ -1031,15 +1033,19 @@ static __init int cgw_module_init(void) ...@@ -1031,15 +1033,19 @@ static __init int cgw_module_init(void)
notifier.notifier_call = cgw_notifier; notifier.notifier_call = cgw_notifier;
register_netdevice_notifier(&notifier); register_netdevice_notifier(&notifier);
if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, 0)) { ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE,
NULL, cgw_dump_jobs, 0);
if (ret) {
unregister_netdevice_notifier(&notifier); unregister_netdevice_notifier(&notifier);
kmem_cache_destroy(cgw_cache); kmem_cache_destroy(cgw_cache);
return -ENOBUFS; return -ENOBUFS;
} }
/* Only the first call to __rtnl_register can fail */ /* Only the first call to rtnl_register_module can fail */
__rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, 0); rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE,
__rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, 0); cgw_create_job, NULL, 0);
rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE,
cgw_remove_job, NULL, 0);
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -1418,9 +1418,12 @@ void __init dn_dev_init(void) ...@@ -1418,9 +1418,12 @@ void __init dn_dev_init(void)
dn_dev_devices_on(); dn_dev_devices_on();
rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL, 0); rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_NEWADDR,
rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, 0); dn_nl_newaddr, NULL, 0);
rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, 0); rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_DELADDR,
dn_nl_deladdr, NULL, 0);
rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_GETADDR,
NULL, dn_nl_dump_ifaddr, 0);
proc_create("decnet_dev", S_IRUGO, init_net.proc_net, &dn_dev_seq_fops); proc_create("decnet_dev", S_IRUGO, init_net.proc_net, &dn_dev_seq_fops);
......
...@@ -792,8 +792,10 @@ void __init dn_fib_init(void) ...@@ -792,8 +792,10 @@ void __init dn_fib_init(void)
register_dnaddr_notifier(&dn_fib_dnaddr_notifier); register_dnaddr_notifier(&dn_fib_dnaddr_notifier);
rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL, 0); rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_NEWROUTE,
rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL, 0); dn_fib_rtm_newroute, NULL, 0);
rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_DELROUTE,
dn_fib_rtm_delroute, NULL, 0);
} }
...@@ -1923,11 +1923,11 @@ void __init dn_route_init(void) ...@@ -1923,11 +1923,11 @@ void __init dn_route_init(void)
&dn_rt_cache_seq_fops); &dn_rt_cache_seq_fops);
#ifdef CONFIG_DECNET_ROUTER #ifdef CONFIG_DECNET_ROUTER
rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_GETROUTE,
dn_fib_dump, 0); dn_cache_getroute, dn_fib_dump, 0);
#else #else
rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, rtnl_register_module(THIS_MODULE, PF_DECnet, RTM_GETROUTE,
dn_cache_dump, 0); dn_cache_getroute, dn_cache_dump, 0);
#endif #endif
} }
......
...@@ -6595,27 +6595,43 @@ int __init addrconf_init(void) ...@@ -6595,27 +6595,43 @@ int __init addrconf_init(void)
rtnl_af_register(&inet6_ops); rtnl_af_register(&inet6_ops);
err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo, err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETLINK,
0); NULL, inet6_dump_ifinfo, 0);
if (err < 0) if (err < 0)
goto errout; goto errout;
/* Only the first call to __rtnl_register can fail */ err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDR,
__rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL, 0); inet6_rtm_newaddr, NULL, 0);
__rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL, 0); if (err < 0)
__rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, goto errout;
inet6_dump_ifaddr, RTNL_FLAG_DOIT_UNLOCKED); err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDR,
__rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, inet6_rtm_deladdr, NULL, 0);
inet6_dump_ifmcaddr, 0); if (err < 0)
__rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, goto errout;
inet6_dump_ifacaddr, 0); err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDR,
__rtnl_register(PF_INET6, RTM_GETNETCONF, inet6_netconf_get_devconf, inet6_rtm_getaddr, inet6_dump_ifaddr,
inet6_netconf_dump_devconf, RTNL_FLAG_DOIT_UNLOCKED); RTNL_FLAG_DOIT_UNLOCKED);
if (err < 0)
goto errout;
err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETMULTICAST,
NULL, inet6_dump_ifmcaddr, 0);
if (err < 0)
goto errout;
err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETANYCAST,
NULL, inet6_dump_ifacaddr, 0);
if (err < 0)
goto errout;
err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETNETCONF,
inet6_netconf_get_devconf,
inet6_netconf_dump_devconf,
RTNL_FLAG_DOIT_UNLOCKED);
if (err < 0)
goto errout;
ipv6_addr_label_rtnl_register(); ipv6_addr_label_rtnl_register();
return 0; return 0;
errout: errout:
rtnl_unregister_all(PF_INET6);
rtnl_af_unregister(&inet6_ops); rtnl_af_unregister(&inet6_ops);
unregister_netdevice_notifier(&ipv6_dev_notf); unregister_netdevice_notifier(&ipv6_dev_notf);
errlo: errlo:
......
...@@ -549,11 +549,10 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, ...@@ -549,11 +549,10 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
void __init ipv6_addr_label_rtnl_register(void) void __init ipv6_addr_label_rtnl_register(void)
{ {
__rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel,
NULL, RTNL_FLAG_DOIT_UNLOCKED); NULL, RTNL_FLAG_DOIT_UNLOCKED);
__rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel,
NULL, RTNL_FLAG_DOIT_UNLOCKED); NULL, RTNL_FLAG_DOIT_UNLOCKED);
__rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get,
ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED); ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED);
} }
...@@ -2142,8 +2142,8 @@ int __init fib6_init(void) ...@@ -2142,8 +2142,8 @@ int __init fib6_init(void)
if (ret) if (ret)
goto out_kmem_cache_create; goto out_kmem_cache_create;
ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib, ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, NULL,
0); inet6_dump_fib, 0);
if (ret) if (ret)
goto out_unregister_subsys; goto out_unregister_subsys;
......
...@@ -4772,11 +4772,20 @@ int __init ip6_route_init(void) ...@@ -4772,11 +4772,20 @@ int __init ip6_route_init(void)
if (ret) if (ret)
goto fib6_rules_init; goto fib6_rules_init;
ret = -ENOBUFS; ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE,
if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, 0) || inet6_rtm_newroute, NULL, 0);
__rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, 0) || if (ret < 0)
__rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, goto out_register_late_subsys;
RTNL_FLAG_DOIT_UNLOCKED))
ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE,
inet6_rtm_delroute, NULL, 0);
if (ret < 0)
goto out_register_late_subsys;
ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE,
inet6_rtm_getroute, NULL,
RTNL_FLAG_DOIT_UNLOCKED);
if (ret < 0)
goto out_register_late_subsys; goto out_register_late_subsys;
ret = register_netdevice_notifier(&ip6_route_dev_notifier); ret = register_netdevice_notifier(&ip6_route_dev_notifier);
...@@ -4794,6 +4803,7 @@ int __init ip6_route_init(void) ...@@ -4794,6 +4803,7 @@ int __init ip6_route_init(void)
return ret; return ret;
out_register_late_subsys: out_register_late_subsys:
rtnl_unregister_all(PF_INET6);
unregister_pernet_subsys(&ip6_route_net_late_ops); unregister_pernet_subsys(&ip6_route_net_late_ops);
fib6_rules_init: fib6_rules_init:
fib6_rules_cleanup(); fib6_rules_cleanup();
......
...@@ -2510,12 +2510,15 @@ static int __init mpls_init(void) ...@@ -2510,12 +2510,15 @@ static int __init mpls_init(void)
rtnl_af_register(&mpls_af_ops); rtnl_af_register(&mpls_af_ops);
rtnl_register(PF_MPLS, RTM_NEWROUTE, mpls_rtm_newroute, NULL, 0); rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_NEWROUTE,
rtnl_register(PF_MPLS, RTM_DELROUTE, mpls_rtm_delroute, NULL, 0); mpls_rtm_newroute, NULL, 0);
rtnl_register(PF_MPLS, RTM_GETROUTE, mpls_getroute, mpls_dump_routes, rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_DELROUTE,
0); mpls_rtm_delroute, NULL, 0);
rtnl_register(PF_MPLS, RTM_GETNETCONF, mpls_netconf_get_devconf, rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETROUTE,
mpls_netconf_dump_devconf, 0); mpls_getroute, mpls_dump_routes, 0);
rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETNETCONF,
mpls_netconf_get_devconf,
mpls_netconf_dump_devconf, 0);
err = ipgre_tunnel_encap_add_mpls_ops(); err = ipgre_tunnel_encap_add_mpls_ops();
if (err) if (err)
pr_err("Can't add mpls over gre tunnel ops\n"); pr_err("Can't add mpls over gre tunnel ops\n");
......
...@@ -299,16 +299,21 @@ static int route_dumpit(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -299,16 +299,21 @@ static int route_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
int __init phonet_netlink_register(void) int __init phonet_netlink_register(void)
{ {
int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, int err = rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_NEWADDR,
NULL, 0); addr_doit, NULL, 0);
if (err) if (err)
return err; return err;
/* Further __rtnl_register() cannot fail */ /* Further rtnl_register_module() cannot fail */
__rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL, 0); rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_DELADDR,
__rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit, 0); addr_doit, NULL, 0);
__rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL, 0); rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_GETADDR,
__rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL, 0); NULL, getaddr_dumpit, 0);
__rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit, 0); rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_NEWROUTE,
route_doit, NULL, 0);
rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_DELROUTE,
route_doit, NULL, 0);
rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_GETROUTE,
NULL, route_dumpit, 0);
return 0; return 0;
} }
...@@ -1116,9 +1116,13 @@ static int __init qrtr_proto_init(void) ...@@ -1116,9 +1116,13 @@ static int __init qrtr_proto_init(void)
return rc; return rc;
} }
rtnl_register(PF_QIPCRTR, RTM_NEWADDR, qrtr_addr_doit, NULL, 0); rc = rtnl_register_module(THIS_MODULE, PF_QIPCRTR, RTM_NEWADDR, qrtr_addr_doit, NULL, 0);
if (rc) {
sock_unregister(qrtr_family.family);
proto_unregister(&qrtr_proto);
}
return 0; return rc;
} }
postcore_initcall(qrtr_proto_init); postcore_initcall(qrtr_proto_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment