Skip to content

Commit

Permalink
Merge branch 'ipv6-cleanups'
Browse files Browse the repository at this point in the history
Cong Wang says:

====================
ipv6: clean up locking code in anycast and mcast

This patchset cleans up the locking code in anycast.c and mcast.c
and makes the refcount code more readable.

Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>

v1 -> v2:
* refactor some code and make it in a separated patch
* update comments
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 13, 2014
2 parents 8801d48 + 1691c63 commit 4361eb6
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 136 deletions.
4 changes: 2 additions & 2 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -2083,8 +2083,8 @@ void __dev_remove_pack(struct packet_type *pt);
void dev_add_offload(struct packet_offload *po);
void dev_remove_offload(struct packet_offload *po);

struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short flags,
unsigned short mask);
struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
unsigned short mask);
struct net_device *dev_get_by_name(struct net *net, const char *name);
struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
struct net_device *__dev_get_by_name(struct net *net, const char *name);
Expand Down
2 changes: 1 addition & 1 deletion include/net/addrconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex,
const struct in6_addr *addr);
void ipv6_sock_ac_close(struct sock *sk);

int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr);
int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr);
int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr);
bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
const struct in6_addr *addr);
Expand Down
14 changes: 8 additions & 6 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -897,31 +897,33 @@ struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type)
EXPORT_SYMBOL(dev_getfirstbyhwtype);

/**
* dev_get_by_flags_rcu - find any device with given flags
* __dev_get_by_flags - find any device with given flags
* @net: the applicable net namespace
* @if_flags: IFF_* values
* @mask: bitmask of bits in if_flags to check
*
* Search for any interface with the given flags. Returns NULL if a device
* is not found or a pointer to the device. Must be called inside
* rcu_read_lock(), and result refcount is unchanged.
* rtnl_lock(), and result refcount is unchanged.
*/

struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short if_flags,
unsigned short mask)
struct net_device *__dev_get_by_flags(struct net *net, unsigned short if_flags,
unsigned short mask)
{
struct net_device *dev, *ret;

ASSERT_RTNL();

ret = NULL;
for_each_netdev_rcu(net, dev) {
for_each_netdev(net, dev) {
if (((dev->flags ^ if_flags) & mask) == 0) {
ret = dev;
break;
}
}
return ret;
}
EXPORT_SYMBOL(dev_get_by_flags_rcu);
EXPORT_SYMBOL(__dev_get_by_flags);

/**
* dev_valid_name - check if name is okay for network device
Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1725,7 +1725,7 @@ static void addrconf_join_anycast(struct inet6_ifaddr *ifp)
ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
if (ipv6_addr_any(&addr))
return;
ipv6_dev_ac_inc(ifp->idev->dev, &addr);
__ipv6_dev_ac_inc(ifp->idev, &addr);
}

/* caller must hold RTNL */
Expand Down
108 changes: 51 additions & 57 deletions net/ipv6/anycast.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@

static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr);

/* Big ac list lock for all the sockets */
static DEFINE_SPINLOCK(ipv6_sk_ac_lock);


/*
* socket join an anycast group
*/
Expand Down Expand Up @@ -78,7 +74,6 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
pac->acl_addr = *addr;

rtnl_lock();
rcu_read_lock();
if (ifindex == 0) {
struct rt6_info *rt;

Expand All @@ -91,11 +86,11 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
goto error;
} else {
/* router, no matching interface: just pick one */
dev = dev_get_by_flags_rcu(net, IFF_UP,
IFF_UP | IFF_LOOPBACK);
dev = __dev_get_by_flags(net, IFF_UP,
IFF_UP | IFF_LOOPBACK);
}
} else
dev = dev_get_by_index_rcu(net, ifindex);
dev = __dev_get_by_index(net, ifindex);

if (dev == NULL) {
err = -ENODEV;
Expand Down Expand Up @@ -127,17 +122,14 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
goto error;
}

err = ipv6_dev_ac_inc(dev, addr);
err = __ipv6_dev_ac_inc(idev, addr);
if (!err) {
spin_lock_bh(&ipv6_sk_ac_lock);
pac->acl_next = np->ipv6_ac_list;
np->ipv6_ac_list = pac;
spin_unlock_bh(&ipv6_sk_ac_lock);
pac = NULL;
}

error:
rcu_read_unlock();
rtnl_unlock();
if (pac)
sock_kfree_s(sk, pac, sizeof(*pac));
Expand All @@ -154,7 +146,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
struct ipv6_ac_socklist *pac, *prev_pac;
struct net *net = sock_net(sk);

spin_lock_bh(&ipv6_sk_ac_lock);
rtnl_lock();
prev_pac = NULL;
for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) {
if ((ifindex == 0 || pac->acl_ifindex == ifindex) &&
Expand All @@ -163,22 +155,17 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
prev_pac = pac;
}
if (!pac) {
spin_unlock_bh(&ipv6_sk_ac_lock);
rtnl_unlock();
return -ENOENT;
}
if (prev_pac)
prev_pac->acl_next = pac->acl_next;
else
np->ipv6_ac_list = pac->acl_next;

spin_unlock_bh(&ipv6_sk_ac_lock);

rtnl_lock();
rcu_read_lock();
dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
dev = __dev_get_by_index(net, pac->acl_ifindex);
if (dev)
ipv6_dev_ac_dec(dev, &pac->acl_addr);
rcu_read_unlock();
rtnl_unlock();

sock_kfree_s(sk, pac, sizeof(*pac));
Expand All @@ -196,30 +183,31 @@ void ipv6_sock_ac_close(struct sock *sk)
if (!np->ipv6_ac_list)
return;

spin_lock_bh(&ipv6_sk_ac_lock);
rtnl_lock();
pac = np->ipv6_ac_list;
np->ipv6_ac_list = NULL;
spin_unlock_bh(&ipv6_sk_ac_lock);

prev_index = 0;
rtnl_lock();
rcu_read_lock();
while (pac) {
struct ipv6_ac_socklist *next = pac->acl_next;

if (pac->acl_ifindex != prev_index) {
dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
dev = __dev_get_by_index(net, pac->acl_ifindex);
prev_index = pac->acl_ifindex;
}
if (dev)
ipv6_dev_ac_dec(dev, &pac->acl_addr);
sock_kfree_s(sk, pac, sizeof(*pac));
pac = next;
}
rcu_read_unlock();
rtnl_unlock();
}

static void aca_get(struct ifacaddr6 *aca)
{
atomic_inc(&aca->aca_refcnt);
}

static void aca_put(struct ifacaddr6 *ac)
{
if (atomic_dec_and_test(&ac->aca_refcnt)) {
Expand All @@ -229,23 +217,40 @@ static void aca_put(struct ifacaddr6 *ac)
}
}

static struct ifacaddr6 *aca_alloc(struct rt6_info *rt,
const struct in6_addr *addr)
{
struct inet6_dev *idev = rt->rt6i_idev;
struct ifacaddr6 *aca;

aca = kzalloc(sizeof(*aca), GFP_ATOMIC);
if (aca == NULL)
return NULL;

aca->aca_addr = *addr;
in6_dev_hold(idev);
aca->aca_idev = idev;
aca->aca_rt = rt;
aca->aca_users = 1;
/* aca_tstamp should be updated upon changes */
aca->aca_cstamp = aca->aca_tstamp = jiffies;
atomic_set(&aca->aca_refcnt, 1);
spin_lock_init(&aca->aca_lock);

return aca;
}

/*
* device anycast group inc (add if not found)
*/
int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr)
int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr)
{
struct ifacaddr6 *aca;
struct inet6_dev *idev;
struct rt6_info *rt;
int err;

ASSERT_RTNL();

idev = in6_dev_get(dev);

if (idev == NULL)
return -EINVAL;

write_lock_bh(&idev->lock);
if (idev->dead) {
err = -ENODEV;
Expand All @@ -260,46 +265,35 @@ int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr)
}
}

/*
* not found: create a new one.
*/

aca = kzalloc(sizeof(struct ifacaddr6), GFP_ATOMIC);

if (aca == NULL) {
err = -ENOMEM;
goto out;
}

rt = addrconf_dst_alloc(idev, addr, true);
if (IS_ERR(rt)) {
kfree(aca);
err = PTR_ERR(rt);
goto out;
}

aca->aca_addr = *addr;
aca->aca_idev = idev;
aca->aca_rt = rt;
aca->aca_users = 1;
/* aca_tstamp should be updated upon changes */
aca->aca_cstamp = aca->aca_tstamp = jiffies;
atomic_set(&aca->aca_refcnt, 2);
spin_lock_init(&aca->aca_lock);
aca = aca_alloc(rt, addr);
if (aca == NULL) {
ip6_rt_put(rt);
err = -ENOMEM;
goto out;
}

aca->aca_next = idev->ac_list;
idev->ac_list = aca;

/* Hold this for addrconf_join_solict() below before we unlock,
* it is already exposed via idev->ac_list.
*/
aca_get(aca);
write_unlock_bh(&idev->lock);

ip6_ins_rt(rt);

addrconf_join_solict(dev, &aca->aca_addr);
addrconf_join_solict(idev->dev, &aca->aca_addr);

aca_put(aca);
return 0;
out:
write_unlock_bh(&idev->lock);
in6_dev_put(idev);
return err;
}

Expand Down Expand Up @@ -341,7 +335,7 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)
return 0;
}

/* called with rcu_read_lock() */
/* called with rtnl_lock() */
static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr)
{
struct inet6_dev *idev = __in6_dev_get(dev);
Expand Down
Loading

0 comments on commit 4361eb6

Please sign in to comment.