{
if (atomic_dec_and_test(&ac->aca_refcnt)) {
in6_dev_put(ac->aca_idev);
+ dst_release(&ac->aca_rt->u.dst);
kfree(ac);
}
}
{
struct ifacaddr6 *aca;
struct inet6_dev *idev;
+ struct rt6_info *rt;
+ int err;
idev = in6_dev_get(dev);
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;
}
}
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;
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) {
}
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)
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
*/