static int snmp6_alloc_dev(struct inet6_dev *idev)
{
- if (snmp_mib_init((void **)idev->stats.ipv6,
+ if (snmp_mib_init((void __percpu **)idev->stats.ipv6,
sizeof(struct ipstats_mib)) < 0)
goto err_ip;
- if (snmp_mib_init((void **)idev->stats.icmpv6,
+ if (snmp_mib_init((void __percpu **)idev->stats.icmpv6,
sizeof(struct icmpv6_mib)) < 0)
goto err_icmp;
- if (snmp_mib_init((void **)idev->stats.icmpv6msg,
+ if (snmp_mib_init((void __percpu **)idev->stats.icmpv6msg,
sizeof(struct icmpv6msg_mib)) < 0)
goto err_icmpmsg;
return 0;
err_icmpmsg:
- snmp_mib_free((void **)idev->stats.icmpv6);
+ snmp_mib_free((void __percpu **)idev->stats.icmpv6);
err_icmp:
- snmp_mib_free((void **)idev->stats.ipv6);
+ snmp_mib_free((void __percpu **)idev->stats.ipv6);
err_ip:
return -ENOMEM;
}
static void snmp6_free_dev(struct inet6_dev *idev)
{
- snmp_mib_free((void **)idev->stats.icmpv6msg);
- snmp_mib_free((void **)idev->stats.icmpv6);
- snmp_mib_free((void **)idev->stats.ipv6);
+ snmp_mib_free((void __percpu **)idev->stats.icmpv6msg);
+ snmp_mib_free((void __percpu **)idev->stats.icmpv6);
+ snmp_mib_free((void __percpu **)idev->stats.ipv6);
}
/* Nobody refers to this device, we may destroy it. */
if (p == &net->ipv6.devconf_dflt->forwarding)
return 0;
- if (!rtnl_trylock())
+ if (!rtnl_trylock()) {
+ /* Restore the original values before restarting */
+ *p = old;
return restart_syscall();
+ }
if (p == &net->ipv6.devconf_all->forwarding) {
__s32 newf = net->ipv6.devconf_all->forwarding;
static inline int ipv6_saddr_preferred(int type)
{
- if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|
- IPV6_ADDR_LOOPBACK|IPV6_ADDR_RESERVED))
+ if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|IPV6_ADDR_LOOPBACK))
return 1;
return 0;
}
static int addrconf_ifdown(struct net_device *dev, int how)
{
struct inet6_dev *idev;
- struct inet6_ifaddr *ifa, **bifa;
+ struct inet6_ifaddr *ifa, *keep_list, **bifa;
struct net *net = dev_net(dev);
int i;
write_lock_bh(&addrconf_hash_lock);
while ((ifa = *bifa) != NULL) {
if (ifa->idev == idev &&
- (how || !(ifa->flags&IFA_F_PERMANENT))) {
+ (how || !(ifa->flags&IFA_F_PERMANENT) ||
+ ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
*bifa = ifa->lst_next;
ifa->lst_next = NULL;
- addrconf_del_timer(ifa);
- in6_ifa_put(ifa);
+ __in6_ifa_put(ifa);
continue;
}
bifa = &ifa->lst_next;
write_lock_bh(&idev->lock);
}
#endif
- bifa = &idev->addr_list;
- while ((ifa = *bifa) != NULL) {
- if (how == 0 && (ifa->flags&IFA_F_PERMANENT)) {
- /* Retain permanent address on admin down */
+ keep_list = NULL;
+ bifa = &keep_list;
+ while ((ifa = idev->addr_list) != NULL) {
+ idev->addr_list = ifa->if_next;
+ ifa->if_next = NULL;
+
+ addrconf_del_timer(ifa);
+
+ /* If just doing link down, and address is permanent
+ and not link-local, then retain it. */
+ if (how == 0 &&
+ (ifa->flags&IFA_F_PERMANENT) &&
+ !(ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
+
+ /* Move to holding list */
+ *bifa = ifa;
bifa = &ifa->if_next;
- /* Restart DAD if needed when link comes back up */
- if ( !((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
- idev->cnf.accept_dad <= 0 ||
- (ifa->flags & IFA_F_NODAD)))
- ifa->flags |= IFA_F_TENTATIVE;
- } else {
- *bifa = ifa->if_next;
- ifa->if_next = NULL;
+ /* If not doing DAD on this address, just keep it. */
+ if ((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
+ idev->cnf.accept_dad <= 0 ||
+ (ifa->flags & IFA_F_NODAD))
+ continue;
+ /* If it was tentative already, no need to notify */
+ if (ifa->flags & IFA_F_TENTATIVE)
+ continue;
+
+ /* Flag it for later restoration when link comes up */
+ ifa->flags |= IFA_F_TENTATIVE;
+ in6_ifa_hold(ifa);
+ } else {
ifa->dead = 1;
- write_unlock_bh(&idev->lock);
+ }
+ write_unlock_bh(&idev->lock);
- __ipv6_ifa_notify(RTM_DELADDR, ifa);
- atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
- in6_ifa_put(ifa);
+ __ipv6_ifa_notify(RTM_DELADDR, ifa);
+ atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
+ in6_ifa_put(ifa);
- write_lock_bh(&idev->lock);
- }
+ write_lock_bh(&idev->lock);
}
+
+ idev->addr_list = keep_list;
+
write_unlock_bh(&idev->lock);
/* Step 5: Discard multicast list */
static void addrconf_rs_timer(unsigned long data)
{
struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
+ struct inet6_dev *idev = ifp->idev;
- if (ifp->idev->cnf.forwarding)
+ read_lock(&idev->lock);
+ if (idev->dead || !(idev->if_flags & IF_READY))
goto out;
- if (ifp->idev->if_flags & IF_RA_RCVD) {
- /*
- * Announcement received after solicitation
- * was sent
- */
+ if (idev->cnf.forwarding)
+ goto out;
+
+ /* Announcement received after solicitation was sent */
+ if (idev->if_flags & IF_RA_RCVD)
goto out;
- }
spin_lock(&ifp->lock);
- if (ifp->probes++ < ifp->idev->cnf.rtr_solicits) {
+ if (ifp->probes++ < idev->cnf.rtr_solicits) {
/* The wait after the last probe can be shorter */
addrconf_mod_timer(ifp, AC_RS,
- (ifp->probes == ifp->idev->cnf.rtr_solicits) ?
- ifp->idev->cnf.rtr_solicit_delay :
- ifp->idev->cnf.rtr_solicit_interval);
+ (ifp->probes == idev->cnf.rtr_solicits) ?
+ idev->cnf.rtr_solicit_delay :
+ idev->cnf.rtr_solicit_interval);
spin_unlock(&ifp->lock);
- ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
+ ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
} else {
spin_unlock(&ifp->lock);
/*
* assumption any longer.
*/
printk(KERN_DEBUG "%s: no IPv6 routers present\n",
- ifp->idev->dev->name);
+ idev->dev->name);
}
out:
+ read_unlock(&idev->lock);
in6_ifa_put(ifp);
}
struct inet6_dev *idev = ifp->idev;
struct in6_addr mcaddr;
- read_lock_bh(&idev->lock);
- if (idev->dead) {
- read_unlock_bh(&idev->lock);
+ read_lock(&idev->lock);
+ if (idev->dead || !(idev->if_flags & IF_READY)) {
+ read_unlock(&idev->lock);
goto out;
}
ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
spin_unlock(&ifp->lock);
- read_unlock_bh(&idev->lock);
+ read_unlock(&idev->lock);
addrconf_dad_completed(ifp);
ifp->probes--;
addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time);
spin_unlock(&ifp->lock);
- read_unlock_bh(&idev->lock);
+ read_unlock(&idev->lock);
/* send a neighbour solicitation for our addr */
addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
);
}
-static inline void __snmp6_fill_stats(u64 *stats, void **mib, int items,
- int bytes)
+static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib,
+ int items, int bytes)
{
int i;
int pad = bytes - sizeof(u64) * items;
{
switch(attrtype) {
case IFLA_INET6_STATS:
- __snmp6_fill_stats(stats, (void **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes);
+ __snmp6_fill_stats(stats, (void __percpu **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes);
break;
case IFLA_INET6_ICMP6STATS:
- __snmp6_fill_stats(stats, (void **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes);
+ __snmp6_fill_stats(stats, (void __percpu **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes);
break;
}
}
{
int *valp = ctl->data;
int val = *valp;
+ loff_t pos = *ppos;
int ret;
ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
if (write)
ret = addrconf_fixup_forwarding(ctl, valp, val);
+ if (ret)
+ *ppos = pos;
return ret;
}
if (p == &net->ipv6.devconf_dflt->disable_ipv6)
return 0;
- if (!rtnl_trylock())
+ if (!rtnl_trylock()) {
+ /* Restore the original values before restarting */
+ *p = old;
return restart_syscall();
+ }
if (p == &net->ipv6.devconf_all->disable_ipv6) {
__s32 newf = net->ipv6.devconf_all->disable_ipv6;
{
int *valp = ctl->data;
int val = *valp;
+ loff_t pos = *ppos;
int ret;
ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
if (write)
ret = addrconf_disable_ipv6(ctl, valp, val);
+ if (ret)
+ *ppos = pos;
return ret;
}