net->ipv4.fibnl = NULL;
 }
 
-static void fib_disable_ip(struct net_device *dev, int force)
+static void fib_disable_ip(struct net_device *dev, unsigned long event)
 {
-       if (fib_sync_down_dev(dev, force))
+       if (fib_sync_down_dev(dev, event))
                fib_flush(dev_net(dev));
        rt_cache_flush(dev_net(dev));
        arp_ifdown(dev);
        case NETDEV_UP:
                fib_add_ifaddr(ifa);
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-               fib_sync_up(dev);
+               fib_sync_up(dev, RTNH_F_DEAD);
 #endif
                atomic_inc(&net->ipv4.dev_addr_genid);
                rt_cache_flush(dev_net(dev));
                        /* Last address was deleted from this interface.
                         * Disable IP.
                         */
-                       fib_disable_ip(dev, 1);
+                       fib_disable_ip(dev, event);
                } else {
                        rt_cache_flush(dev_net(dev));
                }
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct in_device *in_dev;
        struct net *net = dev_net(dev);
+       unsigned int flags;
 
        if (event == NETDEV_UNREGISTER) {
-               fib_disable_ip(dev, 2);
+               fib_disable_ip(dev, event);
                rt_flush_dev(dev);
                return NOTIFY_DONE;
        }
                        fib_add_ifaddr(ifa);
                } endfor_ifa(in_dev);
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-               fib_sync_up(dev);
+               fib_sync_up(dev, RTNH_F_DEAD);
 #endif
                atomic_inc(&net->ipv4.dev_addr_genid);
                rt_cache_flush(net);
                break;
        case NETDEV_DOWN:
-               fib_disable_ip(dev, 0);
+               fib_disable_ip(dev, event);
                break;
-       case NETDEV_CHANGEMTU:
        case NETDEV_CHANGE:
+               flags = dev_get_flags(dev);
+               if (flags & (IFF_RUNNING | IFF_LOWER_UP))
+                       fib_sync_up(dev, RTNH_F_LINKDOWN);
+               else
+                       fib_sync_down_dev(dev, event);
+               /* fall through */
+       case NETDEV_CHANGEMTU:
                rt_cache_flush(net);
                break;
        }
 
 #ifdef CONFIG_IP_ROUTE_CLASSID
                    nh->nh_tclassid != onh->nh_tclassid ||
 #endif
-                   ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_F_DEAD))
+                   ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_COMPARE_MASK))
                        return -1;
                onh++;
        } endfor_nexthops(fi);
                    nfi->fib_type == fi->fib_type &&
                    memcmp(nfi->fib_metrics, fi->fib_metrics,
                           sizeof(u32) * RTAX_MAX) == 0 &&
-                   ((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_F_DEAD) == 0 &&
+                   !((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) &&
                    (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
                        return fi;
        }
                                return -ENODEV;
                        if (!(dev->flags & IFF_UP))
                                return -ENETDOWN;
+                       if (!netif_carrier_ok(dev))
+                               nh->nh_flags |= RTNH_F_LINKDOWN;
                        nh->nh_dev = dev;
                        dev_hold(dev);
                        nh->nh_scope = RT_SCOPE_LINK;
                if (!dev)
                        goto out;
                dev_hold(dev);
+               if (!netif_carrier_ok(dev))
+                       nh->nh_flags |= RTNH_F_LINKDOWN;
                err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN;
        } else {
                struct in_device *in_dev;
                nh->nh_dev = in_dev->dev;
                dev_hold(nh->nh_dev);
                nh->nh_scope = RT_SCOPE_HOST;
+               if (!netif_carrier_ok(nh->nh_dev))
+                       nh->nh_flags |= RTNH_F_LINKDOWN;
                err = 0;
        }
 out:
                if (!nh->nh_dev)
                        goto failure;
        } else {
+               int linkdown = 0;
+
                change_nexthops(fi) {
                        err = fib_check_nh(cfg, fi, nexthop_nh);
                        if (err != 0)
                                goto failure;
+                       if (nexthop_nh->nh_flags & RTNH_F_LINKDOWN)
+                               linkdown++;
                } endfor_nexthops(fi)
+               if (linkdown == fi->fib_nhs)
+                       fi->fib_flags |= RTNH_F_LINKDOWN;
        }
 
        if (fi->fib_prefsrc) {
        return ret;
 }
 
-int fib_sync_down_dev(struct net_device *dev, int force)
+int fib_sync_down_dev(struct net_device *dev, unsigned long event)
 {
        int ret = 0;
        int scope = RT_SCOPE_NOWHERE;
        struct hlist_head *head = &fib_info_devhash[hash];
        struct fib_nh *nh;
 
-       if (force)
+       if (event == NETDEV_UNREGISTER ||
+           event == NETDEV_DOWN)
                scope = -1;
 
        hlist_for_each_entry(nh, head, nh_hash) {
                                dead++;
                        else if (nexthop_nh->nh_dev == dev &&
                                 nexthop_nh->nh_scope != scope) {
-                               nexthop_nh->nh_flags |= RTNH_F_DEAD;
+                               switch (event) {
+                               case NETDEV_DOWN:
+                               case NETDEV_UNREGISTER:
+                                       nexthop_nh->nh_flags |= RTNH_F_DEAD;
+                                       /* fall through */
+                               case NETDEV_CHANGE:
+                                       nexthop_nh->nh_flags |= RTNH_F_LINKDOWN;
+                                       break;
+                               }
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
                                spin_lock_bh(&fib_multipath_lock);
                                fi->fib_power -= nexthop_nh->nh_power;
                                dead++;
                        }
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-                       if (force > 1 && nexthop_nh->nh_dev == dev) {
+                       if (event == NETDEV_UNREGISTER &&
+                           nexthop_nh->nh_dev == dev) {
                                dead = fi->fib_nhs;
                                break;
                        }
 #endif
                } endfor_nexthops(fi)
                if (dead == fi->fib_nhs) {
-                       fi->fib_flags |= RTNH_F_DEAD;
+                       switch (event) {
+                       case NETDEV_DOWN:
+                       case NETDEV_UNREGISTER:
+                               fi->fib_flags |= RTNH_F_DEAD;
+                               /* fall through */
+                       case NETDEV_CHANGE:
+                               fi->fib_flags |= RTNH_F_LINKDOWN;
+                               break;
+                       }
                        ret++;
                }
        }
        return;
 }
 
-#ifdef CONFIG_IP_ROUTE_MULTIPATH
-
 /*
  * Dead device goes up. We wake up dead nexthops.
  * It takes sense only on multipath routes.
  */
-int fib_sync_up(struct net_device *dev)
+int fib_sync_up(struct net_device *dev, unsigned int nh_flags)
 {
        struct fib_info *prev_fi;
        unsigned int hash;
                prev_fi = fi;
                alive = 0;
                change_nexthops(fi) {
-                       if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) {
+                       if (!(nexthop_nh->nh_flags & nh_flags)) {
                                alive++;
                                continue;
                        }
                            !__in_dev_get_rtnl(dev))
                                continue;
                        alive++;
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
                        spin_lock_bh(&fib_multipath_lock);
                        nexthop_nh->nh_power = 0;
-                       nexthop_nh->nh_flags &= ~RTNH_F_DEAD;
+                       nexthop_nh->nh_flags &= ~nh_flags;
                        spin_unlock_bh(&fib_multipath_lock);
+#else
+                       nexthop_nh->nh_flags &= ~nh_flags;
+#endif
                } endfor_nexthops(fi)
 
                if (alive > 0) {
-                       fi->fib_flags &= ~RTNH_F_DEAD;
+                       fi->fib_flags &= ~nh_flags;
                        ret++;
                }
        }
        return ret;
 }
 
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+
 /*
  * The algorithm is suboptimal, but it provides really
  * fair weighted route distribution.