@@ -63,6 +63,7 @@ struct rtnl_link {
63
63
rtnl_doit_func doit ;
64
64
rtnl_dumpit_func dumpit ;
65
65
unsigned int flags ;
66
+ struct rcu_head rcu ;
66
67
};
67
68
68
69
static DEFINE_MUTEX (rtnl_mutex );
@@ -127,7 +128,7 @@ bool lockdep_rtnl_is_held(void)
127
128
EXPORT_SYMBOL (lockdep_rtnl_is_held );
128
129
#endif /* #ifdef CONFIG_PROVE_LOCKING */
129
130
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 ];
131
132
static refcount_t rtnl_msg_handlers_ref [RTNL_FAMILY_MAX + 1 ];
132
133
133
134
static inline int rtm_msgindex (int msgtype )
@@ -144,6 +145,20 @@ static inline int rtm_msgindex(int msgtype)
144
145
return msgindex ;
145
146
}
146
147
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
+
147
162
/**
148
163
* __rtnl_register - Register a rtnetlink message type
149
164
* @protocol: Protocol family or PF_UNSPEC
@@ -166,28 +181,52 @@ int __rtnl_register(int protocol, int msgtype,
166
181
rtnl_doit_func doit , rtnl_dumpit_func dumpit ,
167
182
unsigned int flags )
168
183
{
169
- struct rtnl_link * tab ;
184
+ struct rtnl_link * * tab , * link , * old ;
170
185
int msgindex ;
186
+ int ret = - ENOBUFS ;
171
187
172
188
BUG_ON (protocol < 0 || protocol > RTNL_FAMILY_MAX );
173
189
msgindex = rtm_msgindex (msgtype );
174
190
175
- tab = rcu_dereference_raw (rtnl_msg_handlers [protocol ]);
191
+ rtnl_lock ();
192
+ tab = rtnl_msg_handlers [protocol ];
176
193
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 ;
180
197
198
+ /* ensures we see the 0 stores */
181
199
rcu_assign_pointer (rtnl_msg_handlers [protocol ], tab );
182
200
}
183
201
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 );
184
214
if (doit )
185
- tab [msgindex ].doit = doit ;
215
+ link -> doit = doit ;
216
+ WARN_ON (dumpit && link -> dumpit && link -> dumpit != dumpit );
186
217
if (dumpit )
187
- tab [msgindex ].dumpit = dumpit ;
188
- tab [msgindex ].flags |= flags ;
218
+ link -> dumpit = dumpit ;
189
219
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 ;
191
230
}
192
231
EXPORT_SYMBOL_GPL (__rtnl_register );
193
232
@@ -220,24 +259,25 @@ EXPORT_SYMBOL_GPL(rtnl_register);
220
259
*/
221
260
int rtnl_unregister (int protocol , int msgtype )
222
261
{
223
- struct rtnl_link * handlers ;
262
+ struct rtnl_link * * tab , * link ;
224
263
int msgindex ;
225
264
226
265
BUG_ON (protocol < 0 || protocol > RTNL_FAMILY_MAX );
227
266
msgindex = rtm_msgindex (msgtype );
228
267
229
268
rtnl_lock ();
230
- handlers = rtnl_dereference (rtnl_msg_handlers [protocol ]);
231
- if (!handlers ) {
269
+ tab = rtnl_dereference (rtnl_msg_handlers [protocol ]);
270
+ if (!tab ) {
232
271
rtnl_unlock ();
233
272
return - ENOENT ;
234
273
}
235
274
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 );
239
277
rtnl_unlock ();
240
278
279
+ kfree_rcu (link , rcu );
280
+
241
281
return 0 ;
242
282
}
243
283
EXPORT_SYMBOL_GPL (rtnl_unregister );
@@ -251,20 +291,29 @@ EXPORT_SYMBOL_GPL(rtnl_unregister);
251
291
*/
252
292
void rtnl_unregister_all (int protocol )
253
293
{
254
- struct rtnl_link * handlers ;
294
+ struct rtnl_link * * tab , * link ;
295
+ int msgindex ;
255
296
256
297
BUG_ON (protocol < 0 || protocol > RTNL_FAMILY_MAX );
257
298
258
299
rtnl_lock ();
259
- handlers = rtnl_dereference ( rtnl_msg_handlers [protocol ]) ;
300
+ tab = rtnl_msg_handlers [protocol ];
260
301
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
+ }
261
310
rtnl_unlock ();
262
311
263
312
synchronize_net ();
264
313
265
314
while (refcount_read (& rtnl_msg_handlers_ref [protocol ]) > 1 )
266
315
schedule ();
267
- kfree (handlers );
316
+ kfree (tab );
268
317
}
269
318
EXPORT_SYMBOL_GPL (rtnl_unregister_all );
270
319
@@ -2973,18 +3022,26 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
2973
3022
s_idx = 1 ;
2974
3023
2975
3024
for (idx = 1 ; idx <= RTNL_FAMILY_MAX ; idx ++ ) {
3025
+ struct rtnl_link * * tab ;
2976
3026
int type = cb -> nlh -> nlmsg_type - RTM_BASE ;
2977
- struct rtnl_link * handlers ;
3027
+ struct rtnl_link * link ;
2978
3028
rtnl_dumpit_func dumpit ;
2979
3029
2980
3030
if (idx < s_idx || idx == PF_PACKET )
2981
3031
continue ;
2982
3032
2983
- handlers = rtnl_dereference (rtnl_msg_handlers [idx ]);
2984
- if (!handlers )
3033
+ if (type < 0 || type >= RTM_NR_MSGTYPES )
2985
3034
continue ;
2986
3035
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 ;
2988
3045
if (!dumpit )
2989
3046
continue ;
2990
3047
@@ -4314,7 +4371,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4314
4371
struct netlink_ext_ack * extack )
4315
4372
{
4316
4373
struct net * net = sock_net (skb -> sk );
4317
- struct rtnl_link * handlers ;
4374
+ struct rtnl_link * link ;
4318
4375
int err = - EOPNOTSUPP ;
4319
4376
rtnl_doit_func doit ;
4320
4377
unsigned int flags ;
@@ -4338,32 +4395,20 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4338
4395
if (kind != 2 && !netlink_net_capable (skb , CAP_NET_ADMIN ))
4339
4396
return - EPERM ;
4340
4397
4341
- if (family >= ARRAY_SIZE (rtnl_msg_handlers ))
4342
- family = PF_UNSPEC ;
4343
-
4344
4398
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
-
4351
4399
if (kind == 2 && nlh -> nlmsg_flags & NLM_F_DUMP ) {
4352
4400
struct sock * rtnl ;
4353
4401
rtnl_dumpit_func dumpit ;
4354
4402
u16 min_dump_alloc = 0 ;
4355
4403
4356
- dumpit = READ_ONCE ( handlers [ type ]. dumpit );
4357
- if (!dumpit ) {
4404
+ link = rtnl_get_link ( family , type );
4405
+ if (!link || ! link -> dumpit ) {
4358
4406
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 )
4365
4409
goto err_unlock ;
4366
4410
}
4411
+ dumpit = link -> dumpit ;
4367
4412
4368
4413
refcount_inc (& rtnl_msg_handlers_ref [family ]);
4369
4414
@@ -4384,33 +4429,36 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
4384
4429
return err ;
4385
4430
}
4386
4431
4387
- doit = READ_ONCE ( handlers [ type ]. doit );
4388
- if (!doit ) {
4432
+ link = rtnl_get_link ( family , type );
4433
+ if (!link || ! link -> doit ) {
4389
4434
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 ;
4391
4438
}
4392
4439
4393
- flags = READ_ONCE ( handlers [ type ]. flags ) ;
4440
+ flags = link -> flags ;
4394
4441
if (flags & RTNL_FLAG_DOIT_UNLOCKED ) {
4395
4442
refcount_inc (& rtnl_msg_handlers_ref [family ]);
4396
- doit = READ_ONCE ( handlers [ type ]. doit ) ;
4443
+ doit = link -> doit ;
4397
4444
rcu_read_unlock ();
4398
4445
if (doit )
4399
4446
err = doit (skb , nlh , extack );
4400
4447
refcount_dec (& rtnl_msg_handlers_ref [family ]);
4401
4448
return err ;
4402
4449
}
4403
-
4404
4450
rcu_read_unlock ();
4405
4451
4406
4452
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 );
4413
4456
rtnl_unlock ();
4457
+
4458
+ return err ;
4459
+
4460
+ out_unlock :
4461
+ rcu_read_unlock ();
4414
4462
return err ;
4415
4463
4416
4464
err_unlock :
0 commit comments