Peter Zijlstra <pet...@infradead.org> wrote: > Something like the below would go some way toward sanitizing this stuff; > rcu_assign_pointer() is a store-release, meaning it happens after > everything coming before. > > Therefore, when you observe that tab (through rcu_dereference) you're > guaranteed to see the thing initialized. The memory ordering on the > consume side is through an address dependency; we need to have completed > the load of the tab pointer before we can compute the address of its > members and load from there, these are not things a CPU is allowed to > reorder (lets forget about Alpha). > > Quite possibly, if rtnl_unregister() is called from module unload, this > is broken; in that case we'd need something like: > > rcu_assign_pointer(rtnl_msg_handler[protocol], NULL); > /* > * Ensure nobody can still observe our old protocol handler > * before continuing to free the module that includes the > * functions called from it. > */ > synchronize_rcu(); > > --- > > diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c > index 5ace48926b19..25391c7b9c5d 100644 > --- a/net/core/rtnetlink.c > +++ b/net/core/rtnetlink.c > @@ -63,6 +63,7 @@ struct rtnl_link { > rtnl_doit_func doit; > rtnl_dumpit_func dumpit; > unsigned int flags; > + struct rcu_head rcu; > }; > > static DEFINE_MUTEX(rtnl_mutex); > @@ -172,14 +173,15 @@ int __rtnl_register(int protocol, int msgtype, > BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); > msgindex = rtm_msgindex(msgtype); > > - tab = rcu_dereference_raw(rtnl_msg_handlers[protocol]); > - if (tab == NULL) { > - tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); > - if (tab == NULL) > - return -ENOBUFS; > + if (WARN_ONCE(rtnl_msg_handler[protocol], > + "Double registration for protocol: %d\n", protcol)) > + return -EEXIST;
I would expect this to trigger all the time, due to rtnl_register(AF_INET, RTM_GETROUTE, ... rtnl_register(AF_INET, RTM_GETADDR, ... etc. > @@ -227,15 +231,13 @@ int rtnl_unregister(int protocol, int msgtype) > msgindex = rtm_msgindex(msgtype); > > rtnl_lock(); > - handlers = rtnl_dereference(rtnl_msg_handlers[protocol]); > + handlers = rtnl_msg_handlers[protocol]; > if (!handlers) { > rtnl_unlock(); > return -ENOENT; > } > - > - handlers[msgindex].doit = NULL; > - handlers[msgindex].dumpit = NULL; > - handlers[msgindex].flags = 0; > + rcu_assign_pointer(rtnl_msg_handler[protocol], NULL); > + kfree_rcu(handlers, rcu); This unregisters all handlers of "protocol" instead of "protocol:msgtype".