]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/core/dev.c
WE: Fix set events not propagated
[karo-tx-linux.git] / net / core / dev.c
index 4b24d79414e35d0c18cbc325c679fdfbf340694b..ccefa2473c39df12b4d59aac992cefa1429a8612 100644 (file)
@@ -893,7 +893,8 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf)
                free_page((unsigned long) inuse);
        }
 
-       snprintf(buf, IFNAMSIZ, name, i);
+       if (buf != name)
+               snprintf(buf, IFNAMSIZ, name, i);
        if (!__dev_get_by_name(net, buf))
                return i;
 
@@ -933,6 +934,21 @@ int dev_alloc_name(struct net_device *dev, const char *name)
 }
 EXPORT_SYMBOL(dev_alloc_name);
 
+static int dev_get_valid_name(struct net *net, const char *name, char *buf,
+                             bool fmt)
+{
+       if (!dev_valid_name(name))
+               return -EINVAL;
+
+       if (fmt && strchr(name, '%'))
+               return __dev_alloc_name(net, name, buf);
+       else if (__dev_get_by_name(net, name))
+               return -EEXIST;
+       else if (buf != name)
+               strlcpy(buf, name, IFNAMSIZ);
+
+       return 0;
+}
 
 /**
  *     dev_change_name - change name of a device
@@ -956,22 +972,14 @@ int dev_change_name(struct net_device *dev, const char *newname)
        if (dev->flags & IFF_UP)
                return -EBUSY;
 
-       if (!dev_valid_name(newname))
-               return -EINVAL;
-
        if (strncmp(newname, dev->name, IFNAMSIZ) == 0)
                return 0;
 
        memcpy(oldname, dev->name, IFNAMSIZ);
 
-       if (strchr(newname, '%')) {
-               err = dev_alloc_name(dev, newname);
-               if (err < 0)
-                       return err;
-       } else if (__dev_get_by_name(net, newname))
-               return -EEXIST;
-       else
-               strlcpy(dev->name, newname, IFNAMSIZ);
+       err = dev_get_valid_name(net, newname, dev->name, 1);
+       if (err < 0)
+               return err;
 
 rollback:
        /* For now only devices in the initial network namespace
@@ -999,14 +1007,15 @@ rollback:
        ret = notifier_to_errno(ret);
 
        if (ret) {
-               if (err) {
-                       printk(KERN_ERR
-                              "%s: name change rollback failed: %d.\n",
-                              dev->name, ret);
-               } else {
+               /* err >= 0 after dev_alloc_name() or stores the first errno */
+               if (err >= 0) {
                        err = ret;
                        memcpy(dev->name, oldname, IFNAMSIZ);
                        goto rollback;
+               } else {
+                       printk(KERN_ERR
+                              "%s: name change rollback failed: %d.\n",
+                              dev->name, ret);
                }
        }
 
@@ -1343,6 +1352,7 @@ rollback:
                                nb->notifier_call(nb, NETDEV_DOWN, dev);
                        }
                        nb->notifier_call(nb, NETDEV_UNREGISTER, dev);
+                       nb->notifier_call(nb, NETDEV_UNREGISTER_PERNET, dev);
                }
        }
 
@@ -2277,7 +2287,7 @@ static int ing_filter(struct sk_buff *skb)
        if (MAX_RED_LOOP < ttl++) {
                printk(KERN_WARNING
                       "Redir loop detected Dropping packet (%d->%d)\n",
-                      skb->iif, dev->ifindex);
+                      skb->skb_iif, dev->ifindex);
                return TC_ACT_SHOT;
        }
 
@@ -2385,8 +2395,8 @@ int netif_receive_skb(struct sk_buff *skb)
        if (netpoll_receive_skb(skb))
                return NET_RX_DROP;
 
-       if (!skb->iif)
-               skb->iif = skb->dev->ifindex;
+       if (!skb->skb_iif)
+               skb->skb_iif = skb->dev->ifindex;
 
        null_or_orig = NULL;
        orig_dev = skb->dev;
@@ -4720,7 +4730,8 @@ static void net_set_todo(struct net_device *dev)
 
 static void rollback_registered_many(struct list_head *head)
 {
-       struct net_device *dev;
+       struct net_device *dev, *aux, *fdev;
+       LIST_HEAD(pernet_list);
 
        BUG_ON(dev_boot_phase);
        ASSERT_RTNL();
@@ -4778,8 +4789,24 @@ static void rollback_registered_many(struct list_head *head)
 
        synchronize_net();
 
-       list_for_each_entry(dev, head, unreg_list)
+       list_for_each_entry_safe(dev, aux, head, unreg_list) {
+               int new_net = 1;
+               list_for_each_entry(fdev, &pernet_list, unreg_list) {
+                       if (dev_net(dev) == dev_net(fdev)) {
+                               new_net = 0;
+                               dev_put(dev);
+                               break;
+                       }
+               }
+               if (new_net)
+                       list_move(&dev->unreg_list, &pernet_list);
+       }
+
+       list_for_each_entry_safe(dev, aux, &pernet_list, unreg_list) {
+               call_netdevice_notifiers(NETDEV_UNREGISTER_PERNET, dev);
+               list_move(&dev->unreg_list, head);
                dev_put(dev);
+       }
 }
 
 static void rollback_registered(struct net_device *dev)
@@ -4864,8 +4891,6 @@ EXPORT_SYMBOL(netdev_fix_features);
 
 int register_netdevice(struct net_device *dev)
 {
-       struct hlist_head *head;
-       struct hlist_node *p;
        int ret;
        struct net *net = dev_net(dev);
 
@@ -4894,26 +4919,14 @@ int register_netdevice(struct net_device *dev)
                }
        }
 
-       if (!dev_valid_name(dev->name)) {
-               ret = -EINVAL;
+       ret = dev_get_valid_name(net, dev->name, dev->name, 0);
+       if (ret)
                goto err_uninit;
-       }
 
        dev->ifindex = dev_new_index(net);
        if (dev->iflink == -1)
                dev->iflink = dev->ifindex;
 
-       /* Check for existence of name */
-       head = dev_name_hash(net, dev->name);
-       hlist_for_each(p, head) {
-               struct net_device *d
-                       = hlist_entry(p, struct net_device, name_hlist);
-               if (!strncmp(d->name, dev->name, IFNAMSIZ)) {
-                       ret = -EEXIST;
-                       goto err_uninit;
-               }
-       }
-
        /* Fix illegal checksum combinations */
        if ((dev->features & NETIF_F_HW_CSUM) &&
            (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
@@ -5066,6 +5079,8 @@ static void netdev_wait_allrefs(struct net_device *dev)
 {
        unsigned long rebroadcast_time, warning_time;
 
+       linkwatch_forget_dev(dev);
+
        rebroadcast_time = warning_time = jiffies;
        while (atomic_read(&dev->refcnt) != 0) {
                if (time_after(jiffies, rebroadcast_time + 1 * HZ)) {
@@ -5073,6 +5088,8 @@ static void netdev_wait_allrefs(struct net_device *dev)
 
                        /* Rebroadcast unregister notification */
                        call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+                       /* don't resend NETDEV_UNREGISTER_PERNET, _PERNET users
+                        * should have already handle it the first time */
 
                        if (test_bit(__LINK_STATE_LINKWATCH_PENDING,
                                     &dev->state)) {
@@ -5167,6 +5184,32 @@ void netdev_run_todo(void)
        }
 }
 
+/**
+ *     dev_txq_stats_fold - fold tx_queues stats
+ *     @dev: device to get statistics from
+ *     @stats: struct net_device_stats to hold results
+ */
+void dev_txq_stats_fold(const struct net_device *dev,
+                       struct net_device_stats *stats)
+{
+       unsigned long tx_bytes = 0, tx_packets = 0, tx_dropped = 0;
+       unsigned int i;
+       struct netdev_queue *txq;
+
+       for (i = 0; i < dev->num_tx_queues; i++) {
+               txq = netdev_get_tx_queue(dev, i);
+               tx_bytes   += txq->tx_bytes;
+               tx_packets += txq->tx_packets;
+               tx_dropped += txq->tx_dropped;
+       }
+       if (tx_bytes || tx_packets || tx_dropped) {
+               stats->tx_bytes   = tx_bytes;
+               stats->tx_packets = tx_packets;
+               stats->tx_dropped = tx_dropped;
+       }
+}
+EXPORT_SYMBOL(dev_txq_stats_fold);
+
 /**
  *     dev_get_stats   - get network device statistics
  *     @dev: device to get statistics from
@@ -5181,25 +5224,9 @@ const struct net_device_stats *dev_get_stats(struct net_device *dev)
 
        if (ops->ndo_get_stats)
                return ops->ndo_get_stats(dev);
-       else {
-               unsigned long tx_bytes = 0, tx_packets = 0, tx_dropped = 0;
-               struct net_device_stats *stats = &dev->stats;
-               unsigned int i;
-               struct netdev_queue *txq;
-
-               for (i = 0; i < dev->num_tx_queues; i++) {
-                       txq = netdev_get_tx_queue(dev, i);
-                       tx_bytes   += txq->tx_bytes;
-                       tx_packets += txq->tx_packets;
-                       tx_dropped += txq->tx_dropped;
-               }
-               if (tx_bytes || tx_packets || tx_dropped) {
-                       stats->tx_bytes   = tx_bytes;
-                       stats->tx_packets = tx_packets;
-                       stats->tx_dropped = tx_dropped;
-               }
-               return stats;
-       }
+
+       dev_txq_stats_fold(dev, &dev->stats);
+       return &dev->stats;
 }
 EXPORT_SYMBOL(dev_get_stats);
 
@@ -5280,6 +5307,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
 
        INIT_LIST_HEAD(&dev->napi_list);
        INIT_LIST_HEAD(&dev->unreg_list);
+       INIT_LIST_HEAD(&dev->link_watch_list);
        dev->priv_flags = IFF_XMIT_DST_RELEASE;
        setup(dev);
        strcpy(dev->name, name);
@@ -5347,7 +5375,7 @@ EXPORT_SYMBOL(synchronize_net);
  *     unregister_netdevice_queue - remove device from the kernel
  *     @dev: device
  *     @head: list
-
+ *
  *     This function shuts down a device interface and removes it
  *     from the kernel tables.
  *     If head not NULL, device is queued to be unregistered later.
@@ -5374,6 +5402,10 @@ EXPORT_SYMBOL(unregister_netdevice_queue);
  *     unregister_netdevice_many - unregister many devices
  *     @head: list of devices
  *
+ *     WARNING: Calling this modifies the given list
+ *     (in rollback_registered_many). It may change the order of the elements
+ *     in the list. However, you can assume it does not add or delete elements
+ *     to/from the list.
  */
 void unregister_netdevice_many(struct list_head *head)
 {
@@ -5422,8 +5454,6 @@ EXPORT_SYMBOL(unregister_netdev);
 
 int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
 {
-       char buf[IFNAMSIZ];
-       const char *destname;
        int err;
 
        ASSERT_RTNL();
@@ -5456,20 +5486,11 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
         * we can use it in the destination network namespace.
         */
        err = -EEXIST;
-       destname = dev->name;
-       if (__dev_get_by_name(net, destname)) {
+       if (__dev_get_by_name(net, dev->name)) {
                /* We get here if we can't use the current device name */
                if (!pat)
                        goto out;
-               if (!dev_valid_name(pat))
-                       goto out;
-               if (strchr(pat, '%')) {
-                       if (__dev_alloc_name(net, pat, buf) < 0)
-                               goto out;
-                       destname = buf;
-               } else
-                       destname = pat;
-               if (__dev_get_by_name(net, destname))
+               if (dev_get_valid_name(net, pat, dev->name, 1))
                        goto out;
        }
 
@@ -5493,6 +5514,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
           this device. They should clean all the things.
        */
        call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+       call_netdevice_notifiers(NETDEV_UNREGISTER_PERNET, dev);
 
        /*
         *      Flush the unicast and multicast chains
@@ -5505,10 +5527,6 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
        /* Actually switch the network namespace */
        dev_net_set(dev, net);
 
-       /* Assign the new device name */
-       if (destname != dev->name)
-               strcpy(dev->name, destname);
-
        /* If there is an ifindex conflict assign a new one */
        if (__dev_get_by_index(net, dev->ifindex)) {
                int iflink = (dev->iflink == dev->ifindex);