Skip to content

Commit addf9b9

Browse files
Florian Westphaldavem330
authored andcommitted
net: rtnetlink: use rcu to free rtnl message handlers
rtnetlink is littered with READ_ONCE() because we can have read accesses while another cpu can write to the structure we're reading by (un)registering doit or dumpit handlers. This patch changes this so that (un)registering cpu allocates a new structure and then publishes it via rcu_assign_pointer, i.e. once another cpu can see such pointer no modifications will occur anymore. based on initial patch from Peter Zijlstra. Cc: Peter Zijlstra <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9753c21 commit addf9b9

File tree

1 file changed

+101
-53
lines changed

1 file changed

+101
-53
lines changed

net/core/rtnetlink.c

Lines changed: 101 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -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

6869
static DEFINE_MUTEX(rtnl_mutex);
@@ -127,7 +128,7 @@ bool lockdep_rtnl_is_held(void)
127128
EXPORT_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];
131132
static refcount_t rtnl_msg_handlers_ref[RTNL_FAMILY_MAX + 1];
132133

133134
static 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
}
192231
EXPORT_SYMBOL_GPL(__rtnl_register);
193232

@@ -220,24 +259,25 @@ EXPORT_SYMBOL_GPL(rtnl_register);
220259
*/
221260
int 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
}
243283
EXPORT_SYMBOL_GPL(rtnl_unregister);
@@ -251,20 +291,29 @@ EXPORT_SYMBOL_GPL(rtnl_unregister);
251291
*/
252292
void 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
}
269318
EXPORT_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

44164464
err_unlock:

0 commit comments

Comments
 (0)