@@ -63,6 +63,7 @@ struct rtnl_link {
6363 rtnl_doit_func doit ;
6464 rtnl_dumpit_func dumpit ;
6565 unsigned int flags ;
66+ struct rcu_head rcu ;
6667};
6768
6869static DEFINE_MUTEX (rtnl_mutex );
@@ -127,7 +128,7 @@ bool lockdep_rtnl_is_held(void)
127128EXPORT_SYMBOL (lockdep_rtnl_is_held );
128129#endif /* #ifdef CONFIG_PROVE_LOCKING */
129130
130- static struct rtnl_link __rcu * rtnl_msg_handlers [RTNL_FAMILY_MAX + 1 ];
131+ static struct rtnl_link __rcu * * rtnl_msg_handlers [RTNL_FAMILY_MAX + 1 ];
131132static refcount_t rtnl_msg_handlers_ref [RTNL_FAMILY_MAX + 1 ];
132133
133134static inline int rtm_msgindex (int msgtype )
@@ -144,6 +145,20 @@ static inline int rtm_msgindex(int msgtype)
144145 return msgindex ;
145146}
146147
148+ static 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+
147162/**
148163 * __rtnl_register - Register a rtnetlink message type
149164 * @protocol: Protocol family or PF_UNSPEC
@@ -166,28 +181,52 @@ int __rtnl_register(int protocol, int msgtype,
166181 rtnl_doit_func doit , rtnl_dumpit_func dumpit ,
167182 unsigned int flags )
168183{
169- struct rtnl_link * tab ;
184+ struct rtnl_link * * tab , * link , * old ;
170185 int msgindex ;
186+ int ret = - ENOBUFS ;
171187
172188 BUG_ON (protocol < 0 || protocol > RTNL_FAMILY_MAX );
173189 msgindex = rtm_msgindex (msgtype );
174190
175- tab = rcu_dereference_raw (rtnl_msg_handlers [protocol ]);
191+ rtnl_lock ();
192+ tab = rtnl_msg_handlers [protocol ];
176193 if (tab == NULL ) {
177- tab = kcalloc (RTM_NR_MSGTYPES , sizeof (* tab ), GFP_KERNEL );
178- if (tab == NULL )
179- return - ENOBUFS ;
194+ tab = kcalloc (RTM_NR_MSGTYPES , sizeof (void * ), GFP_KERNEL );
195+ if (! tab )
196+ goto unlock ;
180197
198+ /* ensures we see the 0 stores */
181199 rcu_assign_pointer (rtnl_msg_handlers [protocol ], tab );
182200 }
183201
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 );
184214 if (doit )
185- tab [msgindex ].doit = doit ;
215+ link -> doit = doit ;
216+ WARN_ON (dumpit && link -> dumpit && link -> dumpit != dumpit );
186217 if (dumpit )
187- tab [msgindex ].dumpit = dumpit ;
188- tab [msgindex ].flags |= flags ;
218+ link -> dumpit = dumpit ;
189219
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 );
227+ unlock :
228+ rtnl_unlock ();
229+ return ret ;
191230}
192231EXPORT_SYMBOL_GPL (__rtnl_register );
193232
@@ -220,24 +259,25 @@ EXPORT_SYMBOL_GPL(rtnl_register);
220259 */
221260int rtnl_unregister (int protocol , int msgtype )
222261{
223- struct rtnl_link * handlers ;
262+ struct rtnl_link * * tab , * link ;
224263 int msgindex ;
225264
226265 BUG_ON (protocol < 0 || protocol > RTNL_FAMILY_MAX );
227266 msgindex = rtm_msgindex (msgtype );
228267
229268 rtnl_lock ();
230- handlers = rtnl_dereference (rtnl_msg_handlers [protocol ]);
231- if (!handlers ) {
269+ tab = rtnl_dereference (rtnl_msg_handlers [protocol ]);
270+ if (!tab ) {
232271 rtnl_unlock ();
233272 return - ENOENT ;
234273 }
235274
236- handlers [msgindex ].doit = NULL ;
237- handlers [msgindex ].dumpit = NULL ;
238- handlers [msgindex ].flags = 0 ;
275+ link = tab [msgindex ];
276+ rcu_assign_pointer (tab [msgindex ], NULL );
239277 rtnl_unlock ();
240278
279+ kfree_rcu (link , rcu );
280+
241281 return 0 ;
242282}
243283EXPORT_SYMBOL_GPL (rtnl_unregister );
@@ -251,20 +291,29 @@ EXPORT_SYMBOL_GPL(rtnl_unregister);
251291 */
252292void rtnl_unregister_all (int protocol )
253293{
254- struct rtnl_link * handlers ;
294+ struct rtnl_link * * tab , * link ;
295+ int msgindex ;
255296
256297 BUG_ON (protocol < 0 || protocol > RTNL_FAMILY_MAX );
257298
258299 rtnl_lock ();
259- handlers = rtnl_dereference ( rtnl_msg_handlers [protocol ]) ;
300+ tab = rtnl_msg_handlers [protocol ];
260301 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+ }
261310 rtnl_unlock ();
262311
263312 synchronize_net ();
264313
265314 while (refcount_read (& rtnl_msg_handlers_ref [protocol ]) > 1 )
266315 schedule ();
267- kfree (handlers );
316+ kfree (tab );
268317}
269318EXPORT_SYMBOL_GPL (rtnl_unregister_all );
270319
@@ -2973,18 +3022,26 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
29733022 s_idx = 1 ;
29743023
29753024 for (idx = 1 ; idx <= RTNL_FAMILY_MAX ; idx ++ ) {
3025+ struct rtnl_link * * tab ;
29763026 int type = cb -> nlh -> nlmsg_type - RTM_BASE ;
2977- struct rtnl_link * handlers ;
3027+ struct rtnl_link * link ;
29783028 rtnl_dumpit_func dumpit ;
29793029
29803030 if (idx < s_idx || idx == PF_PACKET )
29813031 continue ;
29823032
2983- handlers = rtnl_dereference (rtnl_msg_handlers [idx ]);
2984- if (!handlers )
3033+ if (type < 0 || type >= RTM_NR_MSGTYPES )
29853034 continue ;
29863035
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 ;
29883045 if (!dumpit )
29893046 continue ;
29903047
@@ -4314,7 +4371,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
43144371 struct netlink_ext_ack * extack )
43154372{
43164373 struct net * net = sock_net (skb -> sk );
4317- struct rtnl_link * handlers ;
4374+ struct rtnl_link * link ;
43184375 int err = - EOPNOTSUPP ;
43194376 rtnl_doit_func doit ;
43204377 unsigned int flags ;
@@ -4338,32 +4395,20 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
43384395 if (kind != 2 && !netlink_net_capable (skb , CAP_NET_ADMIN ))
43394396 return - EPERM ;
43404397
4341- if (family >= ARRAY_SIZE (rtnl_msg_handlers ))
4342- family = PF_UNSPEC ;
4343-
43444398 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-
43514399 if (kind == 2 && nlh -> nlmsg_flags & NLM_F_DUMP ) {
43524400 struct sock * rtnl ;
43534401 rtnl_dumpit_func dumpit ;
43544402 u16 min_dump_alloc = 0 ;
43554403
4356- dumpit = READ_ONCE ( handlers [ type ]. dumpit );
4357- if (!dumpit ) {
4404+ link = rtnl_get_link ( family , type );
4405+ if (!link || ! link -> dumpit ) {
43584406 family = PF_UNSPEC ;
4359- handlers = rcu_dereference (rtnl_msg_handlers [PF_UNSPEC ]);
4360- if (!handlers )
4361- goto err_unlock ;
4362-
4363- dumpit = READ_ONCE (handlers [type ].dumpit );
4364- if (!dumpit )
4407+ link = rtnl_get_link (family , type );
4408+ if (!link || !link -> dumpit )
43654409 goto err_unlock ;
43664410 }
4411+ dumpit = link -> dumpit ;
43674412
43684413 refcount_inc (& rtnl_msg_handlers_ref [family ]);
43694414
@@ -4384,33 +4429,36 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
43844429 return err ;
43854430 }
43864431
4387- doit = READ_ONCE ( handlers [ type ]. doit );
4388- if (!doit ) {
4432+ link = rtnl_get_link ( family , type );
4433+ if (!link || ! link -> doit ) {
43894434 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 ;
43914438 }
43924439
4393- flags = READ_ONCE ( handlers [ type ]. flags ) ;
4440+ flags = link -> flags ;
43944441 if (flags & RTNL_FLAG_DOIT_UNLOCKED ) {
43954442 refcount_inc (& rtnl_msg_handlers_ref [family ]);
4396- doit = READ_ONCE ( handlers [ type ]. doit ) ;
4443+ doit = link -> doit ;
43974444 rcu_read_unlock ();
43984445 if (doit )
43994446 err = doit (skb , nlh , extack );
44004447 refcount_dec (& rtnl_msg_handlers_ref [family ]);
44014448 return err ;
44024449 }
4403-
44044450 rcu_read_unlock ();
44054451
44064452 rtnl_lock ();
4407- handlers = rtnl_dereference (rtnl_msg_handlers [family ]);
4408- if (handlers ) {
4409- doit = READ_ONCE (handlers [type ].doit );
4410- if (doit )
4411- err = doit (skb , nlh , extack );
4412- }
4453+ link = rtnl_get_link (family , type );
4454+ if (link && link -> doit )
4455+ err = link -> doit (skb , nlh , extack );
44134456 rtnl_unlock ();
4457+
4458+ return err ;
4459+
4460+ out_unlock :
4461+ rcu_read_unlock ();
44144462 return err ;
44154463
44164464err_unlock :
0 commit comments