From 7b204afd45820fc21b5983205ad535c6b8088765 Mon Sep 17 00:00:00 2001 From: Robert Olsson Date: Mon, 20 Mar 2006 17:18:53 -0800 Subject: [IPV4]: Use RCU locking in fib_rules. Signed-off-by: Robert Olsson Signed-off-by: David S. Miller --- net/ipv4/fib_rules.c | 113 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 45 deletions(-) (limited to 'net/ipv4/fib_rules.c') diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 0dd4d06e456..7b462a39aa4 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #include @@ -52,7 +54,7 @@ struct fib_rule { - struct fib_rule *r_next; + struct hlist_node hlist; atomic_t r_clntref; u32 r_preference; unsigned char r_table; @@ -75,6 +77,7 @@ struct fib_rule #endif char r_ifname[IFNAMSIZ]; int r_dead; + struct rcu_head rcu; }; static struct fib_rule default_rule = { @@ -85,7 +88,6 @@ static struct fib_rule default_rule = { }; static struct fib_rule main_rule = { - .r_next = &default_rule, .r_clntref = ATOMIC_INIT(2), .r_preference = 0x7FFE, .r_table = RT_TABLE_MAIN, @@ -93,23 +95,24 @@ static struct fib_rule main_rule = { }; static struct fib_rule local_rule = { - .r_next = &main_rule, .r_clntref = ATOMIC_INIT(2), .r_table = RT_TABLE_LOCAL, .r_action = RTN_UNICAST, }; -static struct fib_rule *fib_rules = &local_rule; -static DEFINE_RWLOCK(fib_rules_lock); +struct hlist_head fib_rules; + +/* writer func called from netlink -- rtnl_sem hold*/ int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct rtattr **rta = arg; struct rtmsg *rtm = NLMSG_DATA(nlh); - struct fib_rule *r, **rp; + struct fib_rule *r; + struct hlist_node *node; int err = -ESRCH; - for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) { + hlist_for_each_entry(r, node, &fib_rules, hlist) { if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) && rtm->rtm_src_len == r->r_src_len && 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) if (r == &local_rule) break; - write_lock_bh(&fib_rules_lock); - *rp = r->r_next; + hlist_del_rcu(&r->hlist); r->r_dead = 1; - write_unlock_bh(&fib_rules_lock); fib_rule_put(r); err = 0; break; @@ -150,21 +151,30 @@ static struct fib_table *fib_empty_table(void) return NULL; } +static inline void fib_rule_put_rcu(struct rcu_head *head) +{ + struct fib_rule *r = container_of(head, struct fib_rule, rcu); + kfree(r); +} + void fib_rule_put(struct fib_rule *r) { if (atomic_dec_and_test(&r->r_clntref)) { if (r->r_dead) - kfree(r); + call_rcu(&r->rcu, fib_rule_put_rcu); else printk("Freeing alive rule %p\n", r); } } +/* writer func called from netlink -- rtnl_sem hold*/ + int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct rtattr **rta = arg; struct rtmsg *rtm = NLMSG_DATA(nlh); - struct fib_rule *r, *new_r, **rp; + struct fib_rule *r, *new_r, *last = NULL; + struct hlist_node *node = NULL; unsigned char table_id; 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) if (!new_r) return -ENOMEM; memset(new_r, 0, sizeof(*new_r)); + if (rta[RTA_SRC-1]) memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 4); if (rta[RTA_DST-1]) @@ -220,28 +231,28 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) if (rta[RTA_FLOW-1]) memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4); #endif + r = container_of(fib_rules.first, struct fib_rule, hlist); - rp = &fib_rules; if (!new_r->r_preference) { - r = fib_rules; - if (r && (r = r->r_next) != NULL) { - rp = &fib_rules->r_next; + if (r && r->hlist.next != NULL) { + r = container_of(r->hlist.next, struct fib_rule, hlist); if (r->r_preference) new_r->r_preference = r->r_preference - 1; } } - while ( (r = *rp) != NULL ) { + hlist_for_each_entry(r, node, &fib_rules, hlist) { if (r->r_preference > new_r->r_preference) break; - rp = &r->r_next; + last = r; } - - new_r->r_next = r; atomic_inc(&new_r->r_clntref); - write_lock_bh(&fib_rules_lock); - *rp = new_r; - write_unlock_bh(&fib_rules_lock); + + if (last) + hlist_add_after_rcu(&last->hlist, &new_r->hlist); + else + hlist_add_before_rcu(&new_r->hlist, &r->hlist); + return 0; } @@ -254,30 +265,30 @@ u32 fib_rules_tclass(struct fib_result *res) } #endif +/* callers should hold rtnl semaphore */ static void fib_rules_detach(struct net_device *dev) { + struct hlist_node *node; struct fib_rule *r; - for (r=fib_rules; r; r=r->r_next) { - if (r->r_ifindex == dev->ifindex) { - write_lock_bh(&fib_rules_lock); + hlist_for_each_entry(r, node, &fib_rules, hlist) { + if (r->r_ifindex == dev->ifindex) r->r_ifindex = -1; - write_unlock_bh(&fib_rules_lock); - } + } } +/* callers should hold rtnl semaphore */ + static void fib_rules_attach(struct net_device *dev) { + struct hlist_node *node; struct fib_rule *r; - for (r=fib_rules; r; r=r->r_next) { - if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) { - write_lock_bh(&fib_rules_lock); + hlist_for_each_entry(r, node, &fib_rules, hlist) { + if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) r->r_ifindex = dev->ifindex; - write_unlock_bh(&fib_rules_lock); - } } } @@ -286,14 +297,17 @@ int fib_lookup(const struct flowi *flp, struct fib_result *res) int err; struct fib_rule *r, *policy; struct fib_table *tb; + struct hlist_node *node; u32 daddr = flp->fl4_dst; u32 saddr = flp->fl4_src; FRprintk("Lookup: %u.%u.%u.%u <- %u.%u.%u.%u ", NIPQUAD(flp->fl4_dst), NIPQUAD(flp->fl4_src)); - read_lock(&fib_rules_lock); - for (r = fib_rules; r; r=r->r_next) { + + rcu_read_lock(); + + hlist_for_each_entry_rcu(r, node, &fib_rules, hlist) { if (((saddr^r->r_src) & r->r_srcmask) || ((daddr^r->r_dst) & r->r_dstmask) || (r->r_tos && r->r_tos != flp->fl4_tos) || @@ -309,14 +323,14 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action); policy = r; break; case RTN_UNREACHABLE: - read_unlock(&fib_rules_lock); + rcu_read_unlock(); return -ENETUNREACH; default: case RTN_BLACKHOLE: - read_unlock(&fib_rules_lock); + rcu_read_unlock(); return -EINVAL; case RTN_PROHIBIT: - read_unlock(&fib_rules_lock); + rcu_read_unlock(); return -EACCES; } @@ -327,16 +341,16 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action); res->r = policy; if (policy) atomic_inc(&policy->r_clntref); - read_unlock(&fib_rules_lock); + rcu_read_unlock(); return 0; } if (err < 0 && err != -EAGAIN) { - read_unlock(&fib_rules_lock); + rcu_read_unlock(); return err; } } FRprintk("FAILURE\n"); - read_unlock(&fib_rules_lock); + rcu_read_unlock(); return -ENETUNREACH; } @@ -414,20 +428,25 @@ rtattr_failure: return -1; } +/* callers should hold rtnl semaphore */ + int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) { - int idx; + int idx = 0; int s_idx = cb->args[0]; struct fib_rule *r; + struct hlist_node *node; + + rcu_read_lock(); + hlist_for_each_entry(r, node, &fib_rules, hlist) { - read_lock(&fib_rules_lock); - for (r=fib_rules, idx=0; r; r = r->r_next, idx++) { if (idx < s_idx) continue; if (inet_fill_rule(skb, r, cb, NLM_F_MULTI) < 0) break; + idx++; } - read_unlock(&fib_rules_lock); + rcu_read_unlock(); cb->args[0] = idx; return skb->len; @@ -435,5 +454,9 @@ int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) void __init fib_rules_init(void) { + INIT_HLIST_HEAD(&fib_rules); + hlist_add_head(&local_rule.hlist, &fib_rules); + hlist_add_after(&local_rule.hlist, &main_rule.hlist); + hlist_add_after(&main_rule.hlist, &default_rule.hlist); register_netdevice_notifier(&fib_rules_notifier); } -- cgit v1.2.3-70-g09d2 From d15150f755bb468afe003d1afee0f45a2fc5eeeb Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 20 Mar 2006 17:46:56 -0800 Subject: [IPV4] fib_rules.c: make struct fib_rules static again struct fib_rules became global for no good reason. Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller --- net/ipv4/fib_rules.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/ipv4/fib_rules.c') diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 7b462a39aa4..768e8f5d7da 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -100,7 +100,7 @@ static struct fib_rule local_rule = { .r_action = RTN_UNICAST, }; -struct hlist_head fib_rules; +static struct hlist_head fib_rules; /* writer func called from netlink -- rtnl_sem hold*/ -- cgit v1.2.3-70-g09d2 From a5cdc030030ef5a16e48aebeb65067bdc3120899 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 23 Mar 2006 01:16:06 -0800 Subject: [IPV4]: Add fib rule netlink notifications To really make sense of route notifications in the presence of multiple tables, userspace also needs to be notified about routing rule updates. Notifications are sent to the so far unused RTNLGRP_NOP1 (now RTNLGRP_RULE) group. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 4 +++- net/ipv4/fib_rules.c | 27 ++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'net/ipv4/fib_rules.c') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index d572d537631..df0cdd41085 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -839,6 +839,7 @@ enum #define RTMGRP_IPV4_IFADDR 0x10 #define RTMGRP_IPV4_MROUTE 0x20 #define RTMGRP_IPV4_ROUTE 0x40 +#define RTMGRP_IPV4_RULE 0x80 #define RTMGRP_IPV6_IFADDR 0x100 #define RTMGRP_IPV6_MROUTE 0x200 @@ -869,7 +870,8 @@ enum rtnetlink_groups { #define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_ROUTE, #define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE - RTNLGRP_NOP1, + RTNLGRP_IPV4_RULE, +#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE RTNLGRP_IPV6_IFADDR, #define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_MROUTE, diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 768e8f5d7da..ec566f3e66c 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -104,6 +104,8 @@ static struct hlist_head fib_rules; /* writer func called from netlink -- rtnl_sem hold*/ +static void rtmsg_rule(int, struct fib_rule *); + int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct rtattr **rta = arg; @@ -131,6 +133,7 @@ int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) hlist_del_rcu(&r->hlist); r->r_dead = 1; + rtmsg_rule(RTM_DELRULE, r); fib_rule_put(r); err = 0; break; @@ -253,6 +256,7 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) else hlist_add_before_rcu(&new_r->hlist, &r->hlist); + rtmsg_rule(RTM_NEWRULE, new_r); return 0; } @@ -382,14 +386,14 @@ static struct notifier_block fib_rules_notifier = { static __inline__ int inet_fill_rule(struct sk_buff *skb, struct fib_rule *r, - struct netlink_callback *cb, + u32 pid, u32 seq, int event, unsigned int flags) { struct rtmsg *rtm; struct nlmsghdr *nlh; unsigned char *b = skb->tail; - nlh = NLMSG_NEW_ANSWER(skb, cb, RTM_NEWRULE, sizeof(*rtm), flags); + nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*rtm), flags); rtm = NLMSG_DATA(nlh); rtm->rtm_family = AF_INET; rtm->rtm_dst_len = r->r_dst_len; @@ -430,6 +434,21 @@ rtattr_failure: /* callers should hold rtnl semaphore */ +static void rtmsg_rule(int event, struct fib_rule *r) +{ + int size = NLMSG_SPACE(sizeof(struct rtmsg) + 128); + struct sk_buff *skb = alloc_skb(size, GFP_KERNEL); + + if (!skb) + netlink_set_err(rtnl, 0, RTNLGRP_IPV4_RULE, ENOBUFS); + else if (inet_fill_rule(skb, r, 0, 0, event, 0) < 0) { + kfree_skb(skb); + netlink_set_err(rtnl, 0, RTNLGRP_IPV4_RULE, EINVAL); + } else { + netlink_broadcast(rtnl, skb, 0, RTNLGRP_IPV4_RULE, GFP_KERNEL); + } +} + int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) { int idx = 0; @@ -442,7 +461,9 @@ int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) if (idx < s_idx) continue; - if (inet_fill_rule(skb, r, cb, NLM_F_MULTI) < 0) + if (inet_fill_rule(skb, r, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + RTM_NEWRULE, NLM_F_MULTI) < 0) break; idx++; } -- cgit v1.2.3-70-g09d2