X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv6%2Fanycast.c;h=a0de548c56cae218b1edd97fb3d2935dd7c6454a;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=5b1e4d959f4ad7eaa987f096749b465246609e8f;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 5b1e4d959..a0de548c5 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -293,6 +293,7 @@ static void aca_put(struct ifacaddr6 *ac) { if (atomic_dec_and_test(&ac->aca_refcnt)) { in6_dev_put(ac->aca_idev); + dst_release(&ac->aca_rt->u.dst); kfree(ac); } } @@ -304,6 +305,8 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) { struct ifacaddr6 *aca; struct inet6_dev *idev; + struct rt6_info *rt; + int err; idev = in6_dev_get(dev); @@ -312,17 +315,15 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) write_lock_bh(&idev->lock); if (idev->dead) { - write_unlock_bh(&idev->lock); - in6_dev_put(idev); - return -ENODEV; + err = -ENODEV; + goto out; } for (aca = idev->ac_list; aca; aca = aca->aca_next) { if (ipv6_addr_cmp(&aca->aca_addr, addr) == 0) { aca->aca_users++; - write_unlock_bh(&idev->lock); - in6_dev_put(idev); - return 0; + err = 0; + goto out; } } @@ -333,15 +334,22 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) aca = kmalloc(sizeof(struct ifacaddr6), GFP_ATOMIC); if (aca == NULL) { - write_unlock_bh(&idev->lock); - in6_dev_put(idev); - return -ENOMEM; + err = -ENOMEM; + goto out; + } + + rt = addrconf_dst_alloc(idev, addr, 1); + if (IS_ERR(rt)) { + kfree(aca); + err = PTR_ERR(rt); + goto out; } memset(aca, 0, sizeof(struct ifacaddr6)); ipv6_addr_copy(&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; @@ -352,26 +360,27 @@ int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr) idev->ac_list = aca; write_unlock_bh(&idev->lock); - ip6_rt_addr_add(&aca->aca_addr, dev, 1); + dst_hold(&rt->u.dst); + if (ip6_ins_rt(rt, NULL, NULL)) + dst_release(&rt->u.dst); addrconf_join_solict(dev, &aca->aca_addr); aca_put(aca); return 0; +out: + write_unlock_bh(&idev->lock); + in6_dev_put(idev); + return err; } /* * device anycast group decrement */ -int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) +int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr) { - struct inet6_dev *idev; struct ifacaddr6 *aca, *prev_aca; - idev = in6_dev_get(dev); - if (idev == NULL) - return -ENODEV; - write_lock_bh(&idev->lock); prev_aca = NULL; for (aca = idev->ac_list; aca; aca = aca->aca_next) { @@ -381,12 +390,10 @@ int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) } if (!aca) { write_unlock_bh(&idev->lock); - in6_dev_put(idev); return -ENOENT; } if (--aca->aca_users > 0) { write_unlock_bh(&idev->lock); - in6_dev_put(idev); return 0; } if (prev_aca) @@ -394,15 +401,29 @@ int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) else idev->ac_list = aca->aca_next; write_unlock_bh(&idev->lock); - addrconf_leave_solict(dev, &aca->aca_addr); + addrconf_leave_solict(idev, &aca->aca_addr); - ip6_rt_addr_del(&aca->aca_addr, dev); + dst_hold(&aca->aca_rt->u.dst); + if (ip6_del_rt(aca->aca_rt, NULL, NULL)) + dst_free(&aca->aca_rt->u.dst); + else + dst_release(&aca->aca_rt->u.dst); aca_put(aca); - in6_dev_put(idev); return 0; } +int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) +{ + int ret; + struct inet6_dev *idev = in6_dev_get(dev); + if (idev == NULL) + return -ENODEV; + ret = __ipv6_dev_ac_dec(idev, addr); + in6_dev_put(idev); + return ret; +} + /* * check if the interface has this anycast address */