X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fdevinet.c;h=ec968e4b80482d3c908e4af6f7d9f4a83b7e50c5;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=a85a45964d4230a21498a7672bde2186d8abc368;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index a85a45964..ec968e4b8 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include #include @@ -88,31 +88,31 @@ static void devinet_sysctl_register(struct in_device *in_dev, static void devinet_sysctl_unregister(struct ipv4_devconf *p); #endif -int inet_ifa_count; -int inet_dev_count; - /* Locks all the inet devices. */ -rwlock_t inetdev_lock = RW_LOCK_UNLOCKED; - static struct in_ifaddr *inet_alloc_ifa(void) { struct in_ifaddr *ifa = kmalloc(sizeof(*ifa), GFP_KERNEL); if (ifa) { memset(ifa, 0, sizeof(*ifa)); - inet_ifa_count++; + INIT_RCU_HEAD(&ifa->rcu_head); } return ifa; } -static __inline__ void inet_free_ifa(struct in_ifaddr *ifa) +static void inet_rcu_free_ifa(struct rcu_head *head) { + struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); if (ifa->ifa_dev) - __in_dev_put(ifa->ifa_dev); + in_dev_put(ifa->ifa_dev); kfree(ifa); - inet_ifa_count--; +} + +static inline void inet_free_ifa(struct in_ifaddr *ifa) +{ + call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); } void in_dev_finish_destroy(struct in_device *idev) @@ -129,7 +129,6 @@ void in_dev_finish_destroy(struct in_device *idev) if (!idev->dead) printk("Freeing alive in_device %p\n", idev); else { - inet_dev_count--; kfree(idev); } } @@ -144,24 +143,23 @@ struct in_device *inetdev_init(struct net_device *dev) if (!in_dev) goto out; memset(in_dev, 0, sizeof(*in_dev)); - in_dev->lock = RW_LOCK_UNLOCKED; + INIT_RCU_HEAD(&in_dev->rcu_head); memcpy(&in_dev->cnf, &ipv4_devconf_dflt, sizeof(in_dev->cnf)); in_dev->cnf.sysctl = NULL; in_dev->dev = dev; if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) goto out_kfree; - inet_dev_count++; /* Reference in_dev->dev */ dev_hold(dev); #ifdef CONFIG_SYSCTL neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4", NULL); #endif - write_lock_bh(&inetdev_lock); - dev->ip_ptr = in_dev; + /* Account for reference dev->ip_ptr */ in_dev_hold(in_dev); - write_unlock_bh(&inetdev_lock); + rcu_assign_pointer(dev->ip_ptr, in_dev); + #ifdef CONFIG_SYSCTL devinet_sysctl_register(in_dev, &in_dev->cnf); #endif @@ -176,9 +174,16 @@ out_kfree: goto out; } +static void in_dev_rcu_put(struct rcu_head *head) +{ + struct in_device *idev = container_of(head, struct in_device, rcu_head); + in_dev_put(idev); +} + static void inetdev_destroy(struct in_device *in_dev) { struct in_ifaddr *ifa; + struct net_device *dev; ASSERT_RTNL(); @@ -194,30 +199,31 @@ static void inetdev_destroy(struct in_device *in_dev) #ifdef CONFIG_SYSCTL devinet_sysctl_unregister(&in_dev->cnf); #endif - write_lock_bh(&inetdev_lock); - in_dev->dev->ip_ptr = NULL; - /* in_dev_put following below will kill the in_device */ - write_unlock_bh(&inetdev_lock); + + dev = in_dev->dev; + dev->ip_ptr = NULL; #ifdef CONFIG_SYSCTL neigh_sysctl_unregister(in_dev->arp_parms); #endif neigh_parms_release(&arp_tbl, in_dev->arp_parms); - in_dev_put(in_dev); + arp_ifdown(dev); + + call_rcu(&in_dev->rcu_head, in_dev_rcu_put); } int inet_addr_onlink(struct in_device *in_dev, u32 a, u32 b) { - read_lock(&in_dev->lock); + rcu_read_lock(); for_primary_ifa(in_dev) { if (inet_ifa_match(a, ifa)) { if (!b || inet_ifa_match(b, ifa)) { - read_unlock(&in_dev->lock); + rcu_read_unlock(); return 1; } } } endfor_ifa(in_dev); - read_unlock(&in_dev->lock); + rcu_read_unlock(); return 0; } @@ -241,9 +247,8 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, ifap1 = &ifa->ifa_next; continue; } - write_lock_bh(&in_dev->lock); + *ifap1 = ifa->ifa_next; - write_unlock_bh(&in_dev->lock); rtmsg_ifa(RTM_DELADDR, ifa); notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); @@ -253,9 +258,7 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, /* 2. Unlink it */ - write_lock_bh(&in_dev->lock); *ifap = ifa1->ifa_next; - write_unlock_bh(&in_dev->lock); /* 3. Announce address deletion */ @@ -317,9 +320,7 @@ static int inet_insert_ifa(struct in_ifaddr *ifa) } ifa->ifa_next = *ifap; - write_lock_bh(&in_dev->lock); *ifap = ifa; - write_unlock_bh(&in_dev->lock); /* Send message first, then call notifier. Notifier will trigger FIB update, so that @@ -379,7 +380,7 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, return NULL; } -int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct rtattr **rta = arg; struct in_device *in_dev; @@ -398,7 +399,7 @@ int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) memcmp(RTA_DATA(rta[IFA_LOCAL - 1]), &ifa->ifa_local, 4)) || (rta[IFA_LABEL - 1] && - strcmp(RTA_DATA(rta[IFA_LABEL - 1]), ifa->ifa_label)) || + rtattr_strcmp(rta[IFA_LABEL - 1], ifa->ifa_label)) || (rta[IFA_ADDRESS - 1] && (ifm->ifa_prefixlen != ifa->ifa_prefixlen || !inet_ifa_match(*(u32*)RTA_DATA(rta[IFA_ADDRESS - 1]), @@ -411,7 +412,7 @@ out: return -EADDRNOTAVAIL; } -int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct rtattr **rta = arg; struct net_device *dev; @@ -455,7 +456,7 @@ int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) in_dev_hold(in_dev); ifa->ifa_dev = in_dev; if (rta[IFA_LABEL - 1]) - memcpy(ifa->ifa_label, RTA_DATA(rta[IFA_LABEL - 1]), IFNAMSIZ); + rtattr_strlcpy(ifa->ifa_label, rta[IFA_LABEL - 1], IFNAMSIZ); else memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); @@ -622,7 +623,8 @@ int devinet_ioctl(unsigned int cmd, void __user *arg) ret = -EADDRNOTAVAIL; if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) goto done; - if (!ifa_in_nx_info(ifa, current->nx_info)) + if (vx_flags(VXF_HIDE_NETIF, 0) && + !ifa_in_nx_info(ifa, current->nx_info)) goto done; switch(cmd) { @@ -767,7 +769,8 @@ static int inet_gifconf(struct net_device *dev, char __user *buf, int len) goto out; for (; ifa; ifa = ifa->ifa_next) { - if (!ifa_in_nx_info(ifa, current->nx_info)) + if (vx_flags(VXF_HIDE_NETIF, 0) && + !ifa_in_nx_info(ifa, current->nx_info)) continue; if (!buf) { done += sizeof(ifr); @@ -802,12 +805,11 @@ u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope) u32 addr = 0; struct in_device *in_dev; - read_lock(&inetdev_lock); + rcu_read_lock(); in_dev = __in_dev_get(dev); if (!in_dev) - goto out_unlock_inetdev; + goto no_in_dev; - read_lock(&in_dev->lock); for_primary_ifa(in_dev) { if (ifa->ifa_scope > scope) continue; @@ -818,8 +820,8 @@ u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope) if (!addr) addr = ifa->ifa_local; } endfor_ifa(in_dev); - read_unlock(&in_dev->lock); - read_unlock(&inetdev_lock); +no_in_dev: + rcu_read_unlock(); if (addr) goto out; @@ -829,30 +831,24 @@ u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope) in dev_base list. */ read_lock(&dev_base_lock); - read_lock(&inetdev_lock); + rcu_read_lock(); for (dev = dev_base; dev; dev = dev->next) { if ((in_dev = __in_dev_get(dev)) == NULL) continue; - read_lock(&in_dev->lock); for_primary_ifa(in_dev) { if (ifa->ifa_scope != RT_SCOPE_LINK && ifa->ifa_scope <= scope) { - read_unlock(&in_dev->lock); addr = ifa->ifa_local; goto out_unlock_both; } } endfor_ifa(in_dev); - read_unlock(&in_dev->lock); } out_unlock_both: - read_unlock(&inetdev_lock); read_unlock(&dev_base_lock); + rcu_read_unlock(); out: return addr; -out_unlock_inetdev: - read_unlock(&inetdev_lock); - goto out; } static u32 confirm_addr_indev(struct in_device *in_dev, u32 dst, @@ -905,29 +901,24 @@ u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scop struct in_device *in_dev; if (dev) { - read_lock(&inetdev_lock); - if ((in_dev = __in_dev_get(dev))) { - read_lock(&in_dev->lock); + rcu_read_lock(); + if ((in_dev = __in_dev_get(dev))) addr = confirm_addr_indev(in_dev, dst, local, scope); - read_unlock(&in_dev->lock); - } - read_unlock(&inetdev_lock); + rcu_read_unlock(); return addr; } read_lock(&dev_base_lock); - read_lock(&inetdev_lock); + rcu_read_lock(); for (dev = dev_base; dev; dev = dev->next) { if ((in_dev = __in_dev_get(dev))) { - read_lock(&in_dev->lock); addr = confirm_addr_indev(in_dev, dst, local, scope); - read_unlock(&in_dev->lock); if (addr) break; } } - read_unlock(&inetdev_lock); + rcu_read_unlock(); read_unlock(&dev_base_lock); return addr; @@ -1087,6 +1078,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) struct net_device *dev; struct in_device *in_dev; struct in_ifaddr *ifa; + struct sock *sk = skb->sk; int s_ip_idx, s_idx = cb->args[0]; s_ip_idx = ip_idx = cb->args[1]; @@ -1096,28 +1088,27 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) continue; if (idx > s_idx) s_ip_idx = 0; - read_lock(&inetdev_lock); + rcu_read_lock(); if ((in_dev = __in_dev_get(dev)) == NULL) { - read_unlock(&inetdev_lock); + rcu_read_unlock(); continue; } - read_lock(&in_dev->lock); + for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; ifa = ifa->ifa_next, ip_idx++) { - if (!ifa_in_nx_info(ifa, current->nx_info)) + if (sk && vx_info_flags(sk->sk_vx_info, VXF_HIDE_NETIF, 0) && + !ifa_in_nx_info(ifa, sk->sk_nx_info)) continue; if (ip_idx < s_ip_idx) continue; if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0) { - read_unlock(&in_dev->lock); - read_unlock(&inetdev_lock); + rcu_read_unlock(); goto done; } } - read_unlock(&in_dev->lock); - read_unlock(&inetdev_lock); + rcu_read_unlock(); } done: @@ -1171,11 +1162,11 @@ void inet_forward_change(void) read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) { struct in_device *in_dev; - read_lock(&inetdev_lock); + rcu_read_lock(); in_dev = __in_dev_get(dev); if (in_dev) in_dev->cnf.forwarding = on; - read_unlock(&inetdev_lock); + rcu_read_unlock(); } read_unlock(&dev_base_lock); @@ -1541,6 +1532,5 @@ EXPORT_SYMBOL(devinet_ioctl); EXPORT_SYMBOL(in_dev_finish_destroy); EXPORT_SYMBOL(inet_select_addr); EXPORT_SYMBOL(inetdev_by_index); -EXPORT_SYMBOL(inetdev_lock); EXPORT_SYMBOL(register_inetaddr_notifier); EXPORT_SYMBOL(unregister_inetaddr_notifier);