Skip to content

Commit 7b204af

Browse files
Robert Olssondavem330
authored andcommitted
[IPV4]: Use RCU locking in fib_rules.
Signed-off-by: Robert Olsson <robert.olsson@its.uu.se> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent d8dcffe commit 7b204af

File tree

1 file changed

+68
-45
lines changed

1 file changed

+68
-45
lines changed

net/ipv4/fib_rules.c

Lines changed: 68 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
#include <linux/skbuff.h>
4141
#include <linux/netlink.h>
4242
#include <linux/init.h>
43+
#include <linux/list.h>
44+
#include <linux/rcupdate.h>
4345

4446
#include <net/ip.h>
4547
#include <net/protocol.h>
@@ -52,7 +54,7 @@
5254

5355
struct fib_rule
5456
{
55-
struct fib_rule *r_next;
57+
struct hlist_node hlist;
5658
atomic_t r_clntref;
5759
u32 r_preference;
5860
unsigned char r_table;
@@ -75,6 +77,7 @@ struct fib_rule
7577
#endif
7678
char r_ifname[IFNAMSIZ];
7779
int r_dead;
80+
struct rcu_head rcu;
7881
};
7982

8083
static struct fib_rule default_rule = {
@@ -85,31 +88,31 @@ static struct fib_rule default_rule = {
8588
};
8689

8790
static struct fib_rule main_rule = {
88-
.r_next = &default_rule,
8991
.r_clntref = ATOMIC_INIT(2),
9092
.r_preference = 0x7FFE,
9193
.r_table = RT_TABLE_MAIN,
9294
.r_action = RTN_UNICAST,
9395
};
9496

9597
static struct fib_rule local_rule = {
96-
.r_next = &main_rule,
9798
.r_clntref = ATOMIC_INIT(2),
9899
.r_table = RT_TABLE_LOCAL,
99100
.r_action = RTN_UNICAST,
100101
};
101102

102-
static struct fib_rule *fib_rules = &local_rule;
103-
static DEFINE_RWLOCK(fib_rules_lock);
103+
struct hlist_head fib_rules;
104+
105+
/* writer func called from netlink -- rtnl_sem hold*/
104106

105107
int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
106108
{
107109
struct rtattr **rta = arg;
108110
struct rtmsg *rtm = NLMSG_DATA(nlh);
109-
struct fib_rule *r, **rp;
111+
struct fib_rule *r;
112+
struct hlist_node *node;
110113
int err = -ESRCH;
111114

112-
for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) {
115+
hlist_for_each_entry(r, node, &fib_rules, hlist) {
113116
if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) &&
114117
rtm->rtm_src_len == r->r_src_len &&
115118
rtm->rtm_dst_len == r->r_dst_len &&
@@ -126,10 +129,8 @@ int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
126129
if (r == &local_rule)
127130
break;
128131

129-
write_lock_bh(&fib_rules_lock);
130-
*rp = r->r_next;
132+
hlist_del_rcu(&r->hlist);
131133
r->r_dead = 1;
132-
write_unlock_bh(&fib_rules_lock);
133134
fib_rule_put(r);
134135
err = 0;
135136
break;
@@ -150,21 +151,30 @@ static struct fib_table *fib_empty_table(void)
150151
return NULL;
151152
}
152153

154+
static inline void fib_rule_put_rcu(struct rcu_head *head)
155+
{
156+
struct fib_rule *r = container_of(head, struct fib_rule, rcu);
157+
kfree(r);
158+
}
159+
153160
void fib_rule_put(struct fib_rule *r)
154161
{
155162
if (atomic_dec_and_test(&r->r_clntref)) {
156163
if (r->r_dead)
157-
kfree(r);
164+
call_rcu(&r->rcu, fib_rule_put_rcu);
158165
else
159166
printk("Freeing alive rule %p\n", r);
160167
}
161168
}
162169

170+
/* writer func called from netlink -- rtnl_sem hold*/
171+
163172
int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
164173
{
165174
struct rtattr **rta = arg;
166175
struct rtmsg *rtm = NLMSG_DATA(nlh);
167-
struct fib_rule *r, *new_r, **rp;
176+
struct fib_rule *r, *new_r, *last = NULL;
177+
struct hlist_node *node = NULL;
168178
unsigned char table_id;
169179

170180
if (rtm->rtm_src_len > 32 || rtm->rtm_dst_len > 32 ||
@@ -188,6 +198,7 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
188198
if (!new_r)
189199
return -ENOMEM;
190200
memset(new_r, 0, sizeof(*new_r));
201+
191202
if (rta[RTA_SRC-1])
192203
memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 4);
193204
if (rta[RTA_DST-1])
@@ -220,28 +231,28 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
220231
if (rta[RTA_FLOW-1])
221232
memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4);
222233
#endif
234+
r = container_of(fib_rules.first, struct fib_rule, hlist);
223235

224-
rp = &fib_rules;
225236
if (!new_r->r_preference) {
226-
r = fib_rules;
227-
if (r && (r = r->r_next) != NULL) {
228-
rp = &fib_rules->r_next;
237+
if (r && r->hlist.next != NULL) {
238+
r = container_of(r->hlist.next, struct fib_rule, hlist);
229239
if (r->r_preference)
230240
new_r->r_preference = r->r_preference - 1;
231241
}
232242
}
233243

234-
while ( (r = *rp) != NULL ) {
244+
hlist_for_each_entry(r, node, &fib_rules, hlist) {
235245
if (r->r_preference > new_r->r_preference)
236246
break;
237-
rp = &r->r_next;
247+
last = r;
238248
}
239-
240-
new_r->r_next = r;
241249
atomic_inc(&new_r->r_clntref);
242-
write_lock_bh(&fib_rules_lock);
243-
*rp = new_r;
244-
write_unlock_bh(&fib_rules_lock);
250+
251+
if (last)
252+
hlist_add_after_rcu(&last->hlist, &new_r->hlist);
253+
else
254+
hlist_add_before_rcu(&new_r->hlist, &r->hlist);
255+
245256
return 0;
246257
}
247258

@@ -254,30 +265,30 @@ u32 fib_rules_tclass(struct fib_result *res)
254265
}
255266
#endif
256267

268+
/* callers should hold rtnl semaphore */
257269

258270
static void fib_rules_detach(struct net_device *dev)
259271
{
272+
struct hlist_node *node;
260273
struct fib_rule *r;
261274

262-
for (r=fib_rules; r; r=r->r_next) {
263-
if (r->r_ifindex == dev->ifindex) {
264-
write_lock_bh(&fib_rules_lock);
275+
hlist_for_each_entry(r, node, &fib_rules, hlist) {
276+
if (r->r_ifindex == dev->ifindex)
265277
r->r_ifindex = -1;
266-
write_unlock_bh(&fib_rules_lock);
267-
}
278+
268279
}
269280
}
270281

282+
/* callers should hold rtnl semaphore */
283+
271284
static void fib_rules_attach(struct net_device *dev)
272285
{
286+
struct hlist_node *node;
273287
struct fib_rule *r;
274288

275-
for (r=fib_rules; r; r=r->r_next) {
276-
if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) {
277-
write_lock_bh(&fib_rules_lock);
289+
hlist_for_each_entry(r, node, &fib_rules, hlist) {
290+
if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0)
278291
r->r_ifindex = dev->ifindex;
279-
write_unlock_bh(&fib_rules_lock);
280-
}
281292
}
282293
}
283294

@@ -286,14 +297,17 @@ int fib_lookup(const struct flowi *flp, struct fib_result *res)
286297
int err;
287298
struct fib_rule *r, *policy;
288299
struct fib_table *tb;
300+
struct hlist_node *node;
289301

290302
u32 daddr = flp->fl4_dst;
291303
u32 saddr = flp->fl4_src;
292304

293305
FRprintk("Lookup: %u.%u.%u.%u <- %u.%u.%u.%u ",
294306
NIPQUAD(flp->fl4_dst), NIPQUAD(flp->fl4_src));
295-
read_lock(&fib_rules_lock);
296-
for (r = fib_rules; r; r=r->r_next) {
307+
308+
rcu_read_lock();
309+
310+
hlist_for_each_entry_rcu(r, node, &fib_rules, hlist) {
297311
if (((saddr^r->r_src) & r->r_srcmask) ||
298312
((daddr^r->r_dst) & r->r_dstmask) ||
299313
(r->r_tos && r->r_tos != flp->fl4_tos) ||
@@ -309,14 +323,14 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action);
309323
policy = r;
310324
break;
311325
case RTN_UNREACHABLE:
312-
read_unlock(&fib_rules_lock);
326+
rcu_read_unlock();
313327
return -ENETUNREACH;
314328
default:
315329
case RTN_BLACKHOLE:
316-
read_unlock(&fib_rules_lock);
330+
rcu_read_unlock();
317331
return -EINVAL;
318332
case RTN_PROHIBIT:
319-
read_unlock(&fib_rules_lock);
333+
rcu_read_unlock();
320334
return -EACCES;
321335
}
322336

@@ -327,16 +341,16 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action);
327341
res->r = policy;
328342
if (policy)
329343
atomic_inc(&policy->r_clntref);
330-
read_unlock(&fib_rules_lock);
344+
rcu_read_unlock();
331345
return 0;
332346
}
333347
if (err < 0 && err != -EAGAIN) {
334-
read_unlock(&fib_rules_lock);
348+
rcu_read_unlock();
335349
return err;
336350
}
337351
}
338352
FRprintk("FAILURE\n");
339-
read_unlock(&fib_rules_lock);
353+
rcu_read_unlock();
340354
return -ENETUNREACH;
341355
}
342356

@@ -414,26 +428,35 @@ static __inline__ int inet_fill_rule(struct sk_buff *skb,
414428
return -1;
415429
}
416430

431+
/* callers should hold rtnl semaphore */
432+
417433
int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
418434
{
419-
int idx;
435+
int idx = 0;
420436
int s_idx = cb->args[0];
421437
struct fib_rule *r;
438+
struct hlist_node *node;
439+
440+
rcu_read_lock();
441+
hlist_for_each_entry(r, node, &fib_rules, hlist) {
422442

423-
read_lock(&fib_rules_lock);
424-
for (r=fib_rules, idx=0; r; r = r->r_next, idx++) {
425443
if (idx < s_idx)
426444
continue;
427445
if (inet_fill_rule(skb, r, cb, NLM_F_MULTI) < 0)
428446
break;
447+
idx++;
429448
}
430-
read_unlock(&fib_rules_lock);
449+
rcu_read_unlock();
431450
cb->args[0] = idx;
432451

433452
return skb->len;
434453
}
435454

436455
void __init fib_rules_init(void)
437456
{
457+
INIT_HLIST_HEAD(&fib_rules);
458+
hlist_add_head(&local_rule.hlist, &fib_rules);
459+
hlist_add_after(&local_rule.hlist, &main_rule.hlist);
460+
hlist_add_after(&main_rule.hlist, &default_rule.hlist);
438461
register_netdevice_notifier(&fib_rules_notifier);
439462
}

0 commit comments

Comments
 (0)