Add yet another rtnl_register function. It will be used by modules that can be removed.
The passed module struct is used to take a reference while a netlink dump is in progress to prevent module unload while netlink core can invoke registered dumper function again. Signed-off-by: Florian Westphal <f...@strlen.de> --- include/net/rtnetlink.h | 2 + net/core/rtnetlink.c | 103 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 83 insertions(+), 22 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, rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); void rtnl_register(int protocol, int msgtype, 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); void rtnl_unregister_all(int protocol); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c70f62137dd8..50b483b24845 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -62,6 +62,7 @@ struct rtnl_link { rtnl_doit_func doit; rtnl_dumpit_func dumpit; + struct module *owner; unsigned int flags; }; @@ -143,27 +144,10 @@ static inline int rtm_msgindex(int msgtype) return msgindex; } -/** - * __rtnl_register - Register a rtnetlink message type - * @protocol: Protocol family or PF_UNSPEC - * @msgtype: rtnetlink message type - * @doit: Function pointer called for each request message - * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message - * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions - * - * Registers the specified function pointers (at least one of them has - * to be non-NULL) to be called whenever a request message for the - * specified protocol family and message type is received. - * - * The special protocol family PF_UNSPEC may be used to define fallback - * function pointers for the case when no entry for the specific protocol - * family exists. - * - * Returns 0 on success or a negative error code. - */ -int __rtnl_register(int protocol, int msgtype, - rtnl_doit_func doit, rtnl_dumpit_func dumpit, - unsigned int flags) +static int rtnl_register_internal(struct module *owner, + int protocol, int msgtype, + rtnl_doit_func doit, rtnl_dumpit_func dumpit, + unsigned int flags) { struct rtnl_link *tab; int msgindex; @@ -180,6 +164,12 @@ int __rtnl_register(int protocol, int msgtype, rcu_assign_pointer(rtnl_msg_handlers[protocol], tab); } + WARN_ON(tab[msgindex].owner && tab[msgindex].owner != owner); + + tab[msgindex].owner = owner; + /* make sure owner is always visible first */ + smp_wmb(); + if (doit) tab[msgindex].doit = doit; if (dumpit) @@ -188,6 +178,60 @@ int __rtnl_register(int protocol, int msgtype, return 0; } + +/** + * rtnl_register_module - Register a rtnetlink message type + * + * @owner: module registering the hook (THIS_MODULE) + * @protocol: Protocol family or PF_UNSPEC + * @msgtype: rtnetlink message type + * @doit: Function pointer called for each request message + * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message + * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions + * + * Like rtnl_register, but for use by removable modules. + */ +int rtnl_register_module(struct module *owner, + int protocol, int msgtype, + rtnl_doit_func doit, rtnl_dumpit_func dumpit, + unsigned int flags) +{ + int ret; + + rtnl_lock(); + ret = rtnl_register_internal(owner, protocol, msgtype, + doit, dumpit, flags); + rtnl_unlock(); + + return ret; +} +EXPORT_SYMBOL_GPL(rtnl_register_module); + +/** + * __rtnl_register - Register a rtnetlink message type + * @protocol: Protocol family or PF_UNSPEC + * @msgtype: rtnetlink message type + * @doit: Function pointer called for each request message + * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message + * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions + * + * Registers the specified function pointers (at least one of them has + * to be non-NULL) to be called whenever a request message for the + * specified protocol family and message type is received. + * + * The special protocol family PF_UNSPEC may be used to define fallback + * function pointers for the case when no entry for the specific protocol + * family exists. + * + * Returns 0 on success or a negative error code. + */ +int __rtnl_register(int protocol, int msgtype, + rtnl_doit_func doit, rtnl_dumpit_func dumpit, + unsigned int flags) +{ + return rtnl_register_internal(NULL, protocol, msgtype, + doit, dumpit, flags); +} EXPORT_SYMBOL_GPL(__rtnl_register); /** @@ -235,6 +279,9 @@ int rtnl_unregister(int protocol, int msgtype) handlers[msgindex].doit = NULL; handlers[msgindex].dumpit = NULL; handlers[msgindex].flags = 0; + /* make sure we clear owner last */ + smp_wmb(); + handlers[msgindex].owner = NULL; rtnl_unlock(); return 0; @@ -4346,6 +4393,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, } if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { + struct module *owner = NULL; struct sock *rtnl; rtnl_dumpit_func dumpit; u16 min_dump_alloc = 0; @@ -4360,20 +4408,31 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, dumpit = READ_ONCE(handlers[type].dumpit); if (!dumpit) goto err_unlock; + owner = READ_ONCE(handlers[type].owner); } if (type == RTM_GETLINK - RTM_BASE) min_dump_alloc = rtnl_calcit(skb, nlh); + err = 0; + /* need to do this before rcu_read_unlock() */ + if (!try_module_get(owner)) + err = -EPROTONOSUPPORT; + rcu_read_unlock(); rtnl = net->rtnl; - { + if (err == 0) { struct netlink_dump_control c = { .dump = dumpit, .min_dump_alloc = min_dump_alloc, + .module = owner, }; err = netlink_dump_start(rtnl, skb, nlh, &c); + /* netlink_dump_start() will keep a reference on + * module if dump is still in progress. + */ + module_put(owner); } return err; } -- 2.13.6