Skip to content

Commit

Permalink
[IPV4]: Replace __in_dev_get with __in_dev_get_rcu/rtnl
Browse files Browse the repository at this point in the history
The following patch renames __in_dev_get() to __in_dev_get_rtnl() and
introduces __in_dev_get_rcu() to cover the second case.

1) RCU with refcnt should use in_dev_get().
2) RCU without refcnt should use __in_dev_get_rcu().
3) All others must hold RTNL and use __in_dev_get_rtnl().

There is one exception in net/ipv4/route.c which is in fact a pre-existing
race condition.  I've marked it as such so that we remember to fix it.

This patch is based on suggestions and prior work by Suzanne Wood and
Paul McKenney.

Signed-off-by: Herbert Xu <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
herbertx authored and davem330 committed Oct 3, 2005
1 parent a5e7c21 commit e5ed639
Show file tree
Hide file tree
Showing 24 changed files with 73 additions and 55 deletions.
2 changes: 1 addition & 1 deletion drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2776,7 +2776,7 @@ static u32 bond_glean_dev_ip(struct net_device *dev)
return 0;

rcu_read_lock();
idev = __in_dev_get(dev);
idev = __in_dev_get_rcu(dev);
if (!idev)
goto out;

Expand Down
23 changes: 14 additions & 9 deletions drivers/net/wan/sdlamain.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include <linux/ioport.h> /* request_region(), release_region() */
#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
#include <linux/rcupdate.h>

#include <linux/in.h>
#include <asm/io.h> /* phys_to_virt() */
Expand Down Expand Up @@ -1268,37 +1269,41 @@ unsigned long get_ip_address(struct net_device *dev, int option)

struct in_ifaddr *ifaddr;
struct in_device *in_dev;
unsigned long addr = 0;

if ((in_dev = __in_dev_get(dev)) == NULL){
return 0;
rcu_read_lock();
if ((in_dev = __in_dev_get_rcu(dev)) == NULL){
goto out;
}

if ((ifaddr = in_dev->ifa_list)== NULL ){
return 0;
goto out;
}

switch (option){

case WAN_LOCAL_IP:
return ifaddr->ifa_local;
addr = ifaddr->ifa_local;
break;

case WAN_POINTOPOINT_IP:
return ifaddr->ifa_address;
addr = ifaddr->ifa_address;
break;

case WAN_NETMASK_IP:
return ifaddr->ifa_mask;
addr = ifaddr->ifa_mask;
break;

case WAN_BROADCAST_IP:
return ifaddr->ifa_broadcast;
addr = ifaddr->ifa_broadcast;
break;
default:
return 0;
break;
}

return 0;
out:
rcu_read_unlock();
return addr;
}

void add_gateway(sdla_t *card, struct net_device *dev)
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/wan/syncppp.c
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
u32 addr = 0, mask = ~0; /* FIXME: is the mask correct? */
#ifdef CONFIG_INET
rcu_read_lock();
if ((in_dev = __in_dev_get(dev)) != NULL)
if ((in_dev = __in_dev_get_rcu(dev)) != NULL)
{
for (ifa=in_dev->ifa_list; ifa != NULL;
ifa=ifa->ifa_next) {
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/wireless/strip.c
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@ static unsigned char *strip_make_packet(unsigned char *buffer,
struct in_device *in_dev;

rcu_read_lock();
in_dev = __in_dev_get(strip_info->dev);
in_dev = __in_dev_get_rcu(strip_info->dev);
if (in_dev == NULL) {
rcu_read_unlock();
return NULL;
Expand Down Expand Up @@ -1508,7 +1508,7 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)

brd = addr = 0;
rcu_read_lock();
in_dev = __in_dev_get(strip_info->dev);
in_dev = __in_dev_get_rcu(strip_info->dev);
if (in_dev) {
if (in_dev->ifa_list) {
brd = in_dev->ifa_list->ifa_broadcast;
Expand Down
5 changes: 4 additions & 1 deletion drivers/parisc/led.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/blkdev.h>
#include <linux/rcupdate.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/hardware.h>
Expand Down Expand Up @@ -358,9 +359,10 @@ static __inline__ int led_get_net_activity(void)
/* we are running as tasklet, so locking dev_base
* for reading should be OK */
read_lock(&dev_base_lock);
rcu_read_lock();
for (dev = dev_base; dev; dev = dev->next) {
struct net_device_stats *stats;
struct in_device *in_dev = __in_dev_get(dev);
struct in_device *in_dev = __in_dev_get_rcu(dev);
if (!in_dev || !in_dev->ifa_list)
continue;
if (LOOPBACK(in_dev->ifa_list->ifa_local))
Expand All @@ -371,6 +373,7 @@ static __inline__ int led_get_net_activity(void)
rx_total += stats->rx_packets;
tx_total += stats->tx_packets;
}
rcu_read_unlock();
read_unlock(&dev_base_lock);

retval = 0;
Expand Down
4 changes: 2 additions & 2 deletions drivers/s390/net/qeth_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5200,7 +5200,7 @@ qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid)
if (!card->vlangrp)
return;
rcu_read_lock();
in_dev = __in_dev_get(card->vlangrp->vlan_devices[vid]);
in_dev = __in_dev_get_rcu(card->vlangrp->vlan_devices[vid]);
if (!in_dev)
goto out;
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
Expand Down Expand Up @@ -7725,7 +7725,7 @@ qeth_arp_constructor(struct neighbour *neigh)
goto out;

rcu_read_lock();
in_dev = rcu_dereference(__in_dev_get(dev));
in_dev = __in_dev_get_rcu(dev);
if (in_dev == NULL) {
rcu_read_unlock();
return -EINVAL;
Expand Down
12 changes: 10 additions & 2 deletions include/linux/inetdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,21 +142,29 @@ static __inline__ int bad_mask(u32 mask, u32 addr)

#define endfor_ifa(in_dev) }

static inline struct in_device *__in_dev_get_rcu(const struct net_device *dev)
{
struct in_device *in_dev = dev->ip_ptr;
if (in_dev)
in_dev = rcu_dereference(in_dev);
return in_dev;
}

static __inline__ struct in_device *
in_dev_get(const struct net_device *dev)
{
struct in_device *in_dev;

rcu_read_lock();
in_dev = dev->ip_ptr;
in_dev = __in_dev_get_rcu(dev);
if (in_dev)
atomic_inc(&in_dev->refcnt);
rcu_read_unlock();
return in_dev;
}

static __inline__ struct in_device *
__in_dev_get(const struct net_device *dev)
__in_dev_get_rtnl(const struct net_device *dev)
{
return (struct in_device*)dev->ip_ptr;
}
Expand Down
2 changes: 1 addition & 1 deletion net/atm/clip.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ static int clip_constructor(struct neighbour *neigh)
if (neigh->type != RTN_UNICAST) return -EINVAL;

rcu_read_lock();
in_dev = rcu_dereference(__in_dev_get(dev));
in_dev = __in_dev_get_rcu(dev);
if (!in_dev) {
rcu_read_unlock();
return -EINVAL;
Expand Down
2 changes: 1 addition & 1 deletion net/core/netpoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ int netpoll_setup(struct netpoll *np)

if (!np->local_ip) {
rcu_read_lock();
in_dev = __in_dev_get(ndev);
in_dev = __in_dev_get_rcu(ndev);

if (!in_dev || !in_dev->ifa_list) {
rcu_read_unlock();
Expand Down
2 changes: 1 addition & 1 deletion net/core/pktgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -1667,7 +1667,7 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
struct in_device *in_dev;

rcu_read_lock();
in_dev = __in_dev_get(pkt_dev->odev);
in_dev = __in_dev_get_rcu(pkt_dev->odev);
if (in_dev) {
if (in_dev->ifa_list) {
pkt_dev->saddr_min = in_dev->ifa_list->ifa_address;
Expand Down
2 changes: 1 addition & 1 deletion net/econet/af_econet.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
unsigned long network = 0;

rcu_read_lock();
idev = __in_dev_get(dev);
idev = __in_dev_get_rcu(dev);
if (idev) {
if (idev->ifa_list)
network = ntohl(idev->ifa_list->ifa_address) &
Expand Down
10 changes: 5 additions & 5 deletions net/ipv4/arp.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ static int arp_constructor(struct neighbour *neigh)
neigh->type = inet_addr_type(addr);

rcu_read_lock();
in_dev = rcu_dereference(__in_dev_get(dev));
in_dev = __in_dev_get_rcu(dev);
if (in_dev == NULL) {
rcu_read_unlock();
return -EINVAL;
Expand Down Expand Up @@ -989,8 +989,8 @@ static int arp_req_set(struct arpreq *r, struct net_device * dev)
ipv4_devconf.proxy_arp = 1;
return 0;
}
if (__in_dev_get(dev)) {
__in_dev_get(dev)->cnf.proxy_arp = 1;
if (__in_dev_get_rtnl(dev)) {
__in_dev_get_rtnl(dev)->cnf.proxy_arp = 1;
return 0;
}
return -ENXIO;
Expand Down Expand Up @@ -1095,8 +1095,8 @@ static int arp_req_delete(struct arpreq *r, struct net_device * dev)
ipv4_devconf.proxy_arp = 0;
return 0;
}
if (__in_dev_get(dev)) {
__in_dev_get(dev)->cnf.proxy_arp = 0;
if (__in_dev_get_rtnl(dev)) {
__in_dev_get_rtnl(dev)->cnf.proxy_arp = 0;
return 0;
}
return -ENXIO;
Expand Down
22 changes: 11 additions & 11 deletions net/ipv4/devinet.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ static int inet_insert_ifa(struct in_ifaddr *ifa)

static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
{
struct in_device *in_dev = __in_dev_get(dev);
struct in_device *in_dev = __in_dev_get_rtnl(dev);

ASSERT_RTNL();

Expand Down Expand Up @@ -449,7 +449,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg
goto out;

rc = -ENOBUFS;
if ((in_dev = __in_dev_get(dev)) == NULL) {
if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) {
in_dev = inetdev_init(dev);
if (!in_dev)
goto out;
Expand Down Expand Up @@ -584,7 +584,7 @@ int devinet_ioctl(unsigned int cmd, void __user *arg)
if (colon)
*colon = ':';

if ((in_dev = __in_dev_get(dev)) != NULL) {
if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
if (tryaddrmatch) {
/* Matthias Andree */
/* compare label and address (4.4BSD style) */
Expand Down Expand Up @@ -748,7 +748,7 @@ int devinet_ioctl(unsigned int cmd, void __user *arg)

static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
{
struct in_device *in_dev = __in_dev_get(dev);
struct in_device *in_dev = __in_dev_get_rtnl(dev);
struct in_ifaddr *ifa;
struct ifreq ifr;
int done = 0;
Expand Down Expand Up @@ -791,7 +791,7 @@ u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope)
struct in_device *in_dev;

rcu_read_lock();
in_dev = __in_dev_get(dev);
in_dev = __in_dev_get_rcu(dev);
if (!in_dev)
goto no_in_dev;

Expand All @@ -818,7 +818,7 @@ u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope)
read_lock(&dev_base_lock);
rcu_read_lock();
for (dev = dev_base; dev; dev = dev->next) {
if ((in_dev = __in_dev_get(dev)) == NULL)
if ((in_dev = __in_dev_get_rcu(dev)) == NULL)
continue;

for_primary_ifa(in_dev) {
Expand Down Expand Up @@ -887,7 +887,7 @@ u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scop

if (dev) {
rcu_read_lock();
if ((in_dev = __in_dev_get(dev)))
if ((in_dev = __in_dev_get_rcu(dev)))
addr = confirm_addr_indev(in_dev, dst, local, scope);
rcu_read_unlock();

Expand All @@ -897,7 +897,7 @@ u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scop
read_lock(&dev_base_lock);
rcu_read_lock();
for (dev = dev_base; dev; dev = dev->next) {
if ((in_dev = __in_dev_get(dev))) {
if ((in_dev = __in_dev_get_rcu(dev))) {
addr = confirm_addr_indev(in_dev, dst, local, scope);
if (addr)
break;
Expand Down Expand Up @@ -957,7 +957,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *dev = ptr;
struct in_device *in_dev = __in_dev_get(dev);
struct in_device *in_dev = __in_dev_get_rtnl(dev);

ASSERT_RTNL();

Expand Down Expand Up @@ -1078,7 +1078,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
if (idx > s_idx)
s_ip_idx = 0;
rcu_read_lock();
if ((in_dev = __in_dev_get(dev)) == NULL) {
if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
rcu_read_unlock();
continue;
}
Expand Down Expand Up @@ -1149,7 +1149,7 @@ void inet_forward_change(void)
for (dev = dev_base; dev; dev = dev->next) {
struct in_device *in_dev;
rcu_read_lock();
in_dev = __in_dev_get(dev);
in_dev = __in_dev_get_rcu(dev);
if (in_dev)
in_dev->cnf.forwarding = on;
rcu_read_unlock();
Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/fib_frontend.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,

no_addr = rpf = 0;
rcu_read_lock();
in_dev = __in_dev_get(dev);
in_dev = __in_dev_get_rcu(dev);
if (in_dev) {
no_addr = in_dev->ifa_list == NULL;
rpf = IN_DEV_RPFILTER(in_dev);
Expand Down Expand Up @@ -607,7 +607,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev = ptr;
struct in_device *in_dev = __in_dev_get(dev);
struct in_device *in_dev = __in_dev_get_rtnl(dev);

if (event == NETDEV_UNREGISTER) {
fib_disable_ip(dev, 2);
Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/fib_semantics.c
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,7 @@ fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm,
rta->rta_oif = &dev->ifindex;
if (colon) {
struct in_ifaddr *ifa;
struct in_device *in_dev = __in_dev_get(dev);
struct in_device *in_dev = __in_dev_get_rtnl(dev);
if (!in_dev)
return -ENODEV;
*colon = ':';
Expand Down Expand Up @@ -1268,7 +1268,7 @@ int fib_sync_up(struct net_device *dev)
}
if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
continue;
if (nh->nh_dev != dev || __in_dev_get(dev) == NULL)
if (nh->nh_dev != dev || !__in_dev_get_rtnl(dev))
continue;
alive++;
spin_lock_bh(&fib_multipath_lock);
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/igmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,7 @@ static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
}
if (dev) {
imr->imr_ifindex = dev->ifindex;
idev = __in_dev_get(dev);
idev = __in_dev_get_rtnl(dev);
}
return idev;
}
Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/ip_gre.c
Original file line number Diff line number Diff line change
Expand Up @@ -1104,10 +1104,10 @@ static int ipgre_open(struct net_device *dev)
return -EADDRNOTAVAIL;
dev = rt->u.dst.dev;
ip_rt_put(rt);
if (__in_dev_get(dev) == NULL)
if (__in_dev_get_rtnl(dev) == NULL)
return -EADDRNOTAVAIL;
t->mlink = dev->ifindex;
ip_mc_inc_group(__in_dev_get(dev), t->parms.iph.daddr);
ip_mc_inc_group(__in_dev_get_rtnl(dev), t->parms.iph.daddr);
}
return 0;
}
Expand Down
Loading

0 comments on commit e5ed639

Please sign in to comment.