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)
if (!idev->dead)
printk("Freeing alive in_device %p\n", idev);
else {
- inet_dev_count--;
kfree(idev);
}
}
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);
+ smp_wmb();
+ dev->ip_ptr = in_dev;
+
#ifdef CONFIG_SYSCTL
devinet_sysctl_register(in_dev, &in_dev->cnf);
#endif
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();
#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;
}
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);
/* 2. Unlink it */
- write_lock_bh(&in_dev->lock);
*ifap = ifa1->ifa_next;
- write_unlock_bh(&in_dev->lock);
/* 3. Announce address deletion */
}
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
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) {
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);
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;
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;
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,
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;
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];
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:
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);
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);