Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[powerpc.git] / net / core / dev.c
index 7f31d0f..5a7f20f 100644 (file)
 #include <linux/netpoll.h>
 #include <linux/rcupdate.h>
 #include <linux/delay.h>
-#include <linux/wireless.h>
+#include <net/wext.h>
 #include <net/iw_handler.h>
 #include <asm/current.h>
 #include <linux/audit.h>
 #include <linux/dmaengine.h>
 #include <linux/err.h>
 #include <linux/ctype.h>
+#include <linux/if_arp.h>
 
 /*
  *     The list of packet types we will receive (as opposed to discard)
@@ -156,13 +157,13 @@ static spinlock_t net_dma_event_lock;
 #endif
 
 /*
- * The @dev_base list is protected by @dev_base_lock and the rtnl
+ * The @dev_base_head list is protected by @dev_base_lock and the rtnl
  * semaphore.
  *
  * Pure readers hold dev_base_lock for reading.
  *
  * Writers must hold the rtnl semaphore while they loop through the
- * dev_base list, and hold dev_base_lock for writing when they do the
+ * dev_base_head list, and hold dev_base_lock for writing when they do the
  * actual updates.  This allows pure readers to access the list even
  * while a writer is preparing to update it.
  *
@@ -174,11 +175,10 @@ static spinlock_t net_dma_event_lock;
  * unregister_netdevice(), which must be called with the rtnl
  * semaphore held.
  */
-struct net_device *dev_base;
-static struct net_device **dev_tail = &dev_base;
+LIST_HEAD(dev_base_head);
 DEFINE_RWLOCK(dev_base_lock);
 
-EXPORT_SYMBOL(dev_base);
+EXPORT_SYMBOL(dev_base_head);
 EXPORT_SYMBOL(dev_base_lock);
 
 #define NETDEV_HASHBITS        8
@@ -218,6 +218,73 @@ extern void netdev_unregister_sysfs(struct net_device *);
 #define        netdev_unregister_sysfs(dev)    do { } while(0)
 #endif
 
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+/*
+ * register_netdevice() inits dev->_xmit_lock and sets lockdep class
+ * according to dev->type
+ */
+static const unsigned short netdev_lock_type[] =
+       {ARPHRD_NETROM, ARPHRD_ETHER, ARPHRD_EETHER, ARPHRD_AX25,
+        ARPHRD_PRONET, ARPHRD_CHAOS, ARPHRD_IEEE802, ARPHRD_ARCNET,
+        ARPHRD_APPLETLK, ARPHRD_DLCI, ARPHRD_ATM, ARPHRD_METRICOM,
+        ARPHRD_IEEE1394, ARPHRD_EUI64, ARPHRD_INFINIBAND, ARPHRD_SLIP,
+        ARPHRD_CSLIP, ARPHRD_SLIP6, ARPHRD_CSLIP6, ARPHRD_RSRVD,
+        ARPHRD_ADAPT, ARPHRD_ROSE, ARPHRD_X25, ARPHRD_HWX25,
+        ARPHRD_PPP, ARPHRD_CISCO, ARPHRD_LAPB, ARPHRD_DDCMP,
+        ARPHRD_RAWHDLC, ARPHRD_TUNNEL, ARPHRD_TUNNEL6, ARPHRD_FRAD,
+        ARPHRD_SKIP, ARPHRD_LOOPBACK, ARPHRD_LOCALTLK, ARPHRD_FDDI,
+        ARPHRD_BIF, ARPHRD_SIT, ARPHRD_IPDDP, ARPHRD_IPGRE,
+        ARPHRD_PIMREG, ARPHRD_HIPPI, ARPHRD_ASH, ARPHRD_ECONET,
+        ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
+        ARPHRD_FCFABRIC, ARPHRD_IEEE802_TR, ARPHRD_IEEE80211,
+        ARPHRD_IEEE80211_PRISM, ARPHRD_IEEE80211_RADIOTAP, ARPHRD_VOID,
+        ARPHRD_NONE};
+
+static const char *netdev_lock_name[] =
+       {"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
+        "_xmit_PRONET", "_xmit_CHAOS", "_xmit_IEEE802", "_xmit_ARCNET",
+        "_xmit_APPLETLK", "_xmit_DLCI", "_xmit_ATM", "_xmit_METRICOM",
+        "_xmit_IEEE1394", "_xmit_EUI64", "_xmit_INFINIBAND", "_xmit_SLIP",
+        "_xmit_CSLIP", "_xmit_SLIP6", "_xmit_CSLIP6", "_xmit_RSRVD",
+        "_xmit_ADAPT", "_xmit_ROSE", "_xmit_X25", "_xmit_HWX25",
+        "_xmit_PPP", "_xmit_CISCO", "_xmit_LAPB", "_xmit_DDCMP",
+        "_xmit_RAWHDLC", "_xmit_TUNNEL", "_xmit_TUNNEL6", "_xmit_FRAD",
+        "_xmit_SKIP", "_xmit_LOOPBACK", "_xmit_LOCALTLK", "_xmit_FDDI",
+        "_xmit_BIF", "_xmit_SIT", "_xmit_IPDDP", "_xmit_IPGRE",
+        "_xmit_PIMREG", "_xmit_HIPPI", "_xmit_ASH", "_xmit_ECONET",
+        "_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
+        "_xmit_FCFABRIC", "_xmit_IEEE802_TR", "_xmit_IEEE80211",
+        "_xmit_IEEE80211_PRISM", "_xmit_IEEE80211_RADIOTAP", "_xmit_VOID",
+        "_xmit_NONE"};
+
+static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
+
+static inline unsigned short netdev_lock_pos(unsigned short dev_type)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(netdev_lock_type); i++)
+               if (netdev_lock_type[i] == dev_type)
+                       return i;
+       /* the last key is used by default */
+       return ARRAY_SIZE(netdev_lock_type) - 1;
+}
+
+static inline void netdev_set_lockdep_class(spinlock_t *lock,
+                                           unsigned short dev_type)
+{
+       int i;
+
+       i = netdev_lock_pos(dev_type);
+       lockdep_set_class_and_name(lock, &netdev_xmit_lock_key[i],
+                                  netdev_lock_name[i]);
+}
+#else
+static inline void netdev_set_lockdep_class(spinlock_t *lock,
+                                           unsigned short dev_type)
+{
+}
+#endif
 
 /*******************************************************************************
 
@@ -225,12 +292,6 @@ extern void netdev_unregister_sysfs(struct net_device *);
 
 *******************************************************************************/
 
-/*
- *     For efficiency
- */
-
-static int netdev_nit;
-
 /*
  *     Add a protocol ID to the list. Now that the input handler is
  *     smarter we can dispense with all the messy stuff that used to be
@@ -265,10 +326,9 @@ void dev_add_pack(struct packet_type *pt)
        int hash;
 
        spin_lock_bh(&ptype_lock);
-       if (pt->type == htons(ETH_P_ALL)) {
-               netdev_nit++;
+       if (pt->type == htons(ETH_P_ALL))
                list_add_rcu(&pt->list, &ptype_all);
-       else {
+       else {
                hash = ntohs(pt->type) & 15;
                list_add_rcu(&pt->list, &ptype_base[hash]);
        }
@@ -295,10 +355,9 @@ void __dev_remove_pack(struct packet_type *pt)
 
        spin_lock_bh(&ptype_lock);
 
-       if (pt->type == htons(ETH_P_ALL)) {
-               netdev_nit--;
+       if (pt->type == htons(ETH_P_ALL))
                head = &ptype_all;
-       else
+       else
                head = &ptype_base[ntohs(pt->type) & 15];
 
        list_for_each_entry(pt1, head, list) {
@@ -575,26 +634,38 @@ struct net_device *dev_getbyhwaddr(unsigned short type, char *ha)
 
        ASSERT_RTNL();
 
-       for (dev = dev_base; dev; dev = dev->next)
+       for_each_netdev(dev)
                if (dev->type == type &&
                    !memcmp(dev->dev_addr, ha, dev->addr_len))
-                       break;
-       return dev;
+                       return dev;
+
+       return NULL;
 }
 
 EXPORT_SYMBOL(dev_getbyhwaddr);
 
+struct net_device *__dev_getfirstbyhwtype(unsigned short type)
+{
+       struct net_device *dev;
+
+       ASSERT_RTNL();
+       for_each_netdev(dev)
+               if (dev->type == type)
+                       return dev;
+
+       return NULL;
+}
+
+EXPORT_SYMBOL(__dev_getfirstbyhwtype);
+
 struct net_device *dev_getfirstbyhwtype(unsigned short type)
 {
        struct net_device *dev;
 
        rtnl_lock();
-       for (dev = dev_base; dev; dev = dev->next) {
-               if (dev->type == type) {
-                       dev_hold(dev);
-                       break;
-               }
-       }
+       dev = __dev_getfirstbyhwtype(type);
+       if (dev)
+               dev_hold(dev);
        rtnl_unlock();
        return dev;
 }
@@ -614,17 +685,19 @@ EXPORT_SYMBOL(dev_getfirstbyhwtype);
 
 struct net_device * dev_get_by_flags(unsigned short if_flags, unsigned short mask)
 {
-       struct net_device *dev;
+       struct net_device *dev, *ret;
 
+       ret = NULL;
        read_lock(&dev_base_lock);
-       for (dev = dev_base; dev != NULL; dev = dev->next) {
+       for_each_netdev(dev) {
                if (((dev->flags ^ if_flags) & mask) == 0) {
                        dev_hold(dev);
+                       ret = dev;
                        break;
                }
        }
        read_unlock(&dev_base_lock);
-       return dev;
+       return ret;
 }
 
 /**
@@ -690,7 +763,7 @@ int dev_alloc_name(struct net_device *dev, const char *name)
                if (!inuse)
                        return -ENOMEM;
 
-               for (d = dev_base; d; d = d->next) {
+               for_each_netdev(d) {
                        if (!sscanf(d->name, name, &i))
                                continue;
                        if (i < 0 || i >= max_netdevices)
@@ -972,7 +1045,7 @@ int register_netdevice_notifier(struct notifier_block *nb)
        rtnl_lock();
        err = raw_notifier_chain_register(&netdev_chain, nb);
        if (!err) {
-               for (dev = dev_base; dev; dev = dev->next) {
+               for_each_netdev(dev) {
                        nb->notifier_call(nb, NETDEV_REGISTER, dev);
 
                        if (dev->flags & IFF_UP)
@@ -1210,7 +1283,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features)
        skb->mac_len = skb->network_header - skb->mac_header;
        __skb_pull(skb, skb->mac_len);
 
-       if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+       if (WARN_ON(skb->ip_summed != CHECKSUM_PARTIAL)) {
                if (skb_header_cloned(skb) &&
                    (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
                        return ERR_PTR(err);
@@ -1330,7 +1403,7 @@ static int dev_gso_segment(struct sk_buff *skb)
 int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        if (likely(!skb->next)) {
-               if (netdev_nit)
+               if (!list_empty(&ptype_all))
                        dev_queue_xmit_nit(skb, dev);
 
                if (netif_needs_gso(dev, skb)) {
@@ -1747,10 +1820,10 @@ static int ing_filter(struct sk_buff *skb)
 
                skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_INGRESS);
 
-               spin_lock(&dev->queue_lock);
+               spin_lock(&dev->ingress_lock);
                if ((q = dev->qdisc_ingress) != NULL)
                        result = q->enqueue(skb, q);
-               spin_unlock(&dev->queue_lock);
+               spin_unlock(&dev->ingress_lock);
 
        }
 
@@ -2046,7 +2119,7 @@ static int dev_ifconf(char __user *arg)
         */
 
        total = 0;
-       for (dev = dev_base; dev; dev = dev->next) {
+       for_each_netdev(dev) {
                for (i = 0; i < NPROTO; i++) {
                        if (gifconf_list[i]) {
                                int done;
@@ -2078,26 +2151,28 @@ static int dev_ifconf(char __user *arg)
  *     This is invoked by the /proc filesystem handler to display a device
  *     in detail.
  */
-static struct net_device *dev_get_idx(loff_t pos)
+void *dev_seq_start(struct seq_file *seq, loff_t *pos)
 {
+       loff_t off;
        struct net_device *dev;
-       loff_t i;
 
-       for (i = 0, dev = dev_base; dev && i < pos; ++i, dev = dev->next);
+       read_lock(&dev_base_lock);
+       if (!*pos)
+               return SEQ_START_TOKEN;
 
-       return i == pos ? dev : NULL;
-}
+       off = 1;
+       for_each_netdev(dev)
+               if (off++ == *pos)
+                       return dev;
 
-void *dev_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       read_lock(&dev_base_lock);
-       return *pos ? dev_get_idx(*pos - 1) : SEQ_START_TOKEN;
+       return NULL;
 }
 
 void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        ++*pos;
-       return v == SEQ_START_TOKEN ? dev_base : ((struct net_device *)v)->next;
+       return v == SEQ_START_TOKEN ?
+               first_net_device() : next_net_device((struct net_device *)v);
 }
 
 void dev_seq_stop(struct seq_file *seq, void *v)
@@ -2109,26 +2184,23 @@ static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
 {
        struct net_device_stats *stats = dev->get_stats(dev);
 
-       if (stats) {
-               seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
-                               "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
-                          dev->name, stats->rx_bytes, stats->rx_packets,
-                          stats->rx_errors,
-                          stats->rx_dropped + stats->rx_missed_errors,
-                          stats->rx_fifo_errors,
-                          stats->rx_length_errors + stats->rx_over_errors +
-                            stats->rx_crc_errors + stats->rx_frame_errors,
-                          stats->rx_compressed, stats->multicast,
-                          stats->tx_bytes, stats->tx_packets,
-                          stats->tx_errors, stats->tx_dropped,
-                          stats->tx_fifo_errors, stats->collisions,
-                          stats->tx_carrier_errors +
-                            stats->tx_aborted_errors +
-                            stats->tx_window_errors +
-                            stats->tx_heartbeat_errors,
-                          stats->tx_compressed);
-       } else
-               seq_printf(seq, "%6s: No statistics available.\n", dev->name);
+       seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
+                  "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
+                  dev->name, stats->rx_bytes, stats->rx_packets,
+                  stats->rx_errors,
+                  stats->rx_dropped + stats->rx_missed_errors,
+                  stats->rx_fifo_errors,
+                  stats->rx_length_errors + stats->rx_over_errors +
+                   stats->rx_crc_errors + stats->rx_frame_errors,
+                  stats->rx_compressed, stats->multicast,
+                  stats->tx_bytes, stats->tx_packets,
+                  stats->tx_errors, stats->tx_dropped,
+                  stats->tx_fifo_errors, stats->collisions,
+                  stats->tx_carrier_errors +
+                   stats->tx_aborted_errors +
+                   stats->tx_window_errors +
+                   stats->tx_heartbeat_errors,
+                  stats->tx_compressed);
 }
 
 /*
@@ -2356,12 +2428,6 @@ static const struct file_operations ptype_seq_fops = {
 };
 
 
-#ifdef CONFIG_WIRELESS_EXT
-extern int wireless_proc_init(void);
-#else
-#define wireless_proc_init() 0
-#endif
-
 static int __init dev_proc_init(void)
 {
        int rc = -ENOMEM;
@@ -2373,15 +2439,15 @@ static int __init dev_proc_init(void)
        if (!proc_net_fops_create("ptype", S_IRUGO, &ptype_seq_fops))
                goto out_dev2;
 
-       if (wireless_proc_init())
+       if (wext_proc_init())
                goto out_softnet;
        rc = 0;
 out:
        return rc;
 out_softnet:
-       proc_net_remove("softnet_stat");
-out_dev2:
        proc_net_remove("ptype");
+out_dev2:
+       proc_net_remove("softnet_stat");
 out_dev:
        proc_net_remove("dev");
        goto out;
@@ -2931,29 +2997,9 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
                                        ret = -EFAULT;
                                return ret;
                        }
-#ifdef CONFIG_WIRELESS_EXT
                        /* Take care of Wireless Extensions */
-                       if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
-                               /* If command is `set a parameter', or
-                                * `get the encoding parameters', check if
-                                * the user has the right to do it */
-                               if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE
-                                   || cmd == SIOCGIWENCODEEXT) {
-                                       if (!capable(CAP_NET_ADMIN))
-                                               return -EPERM;
-                               }
-                               dev_load(ifr.ifr_name);
-                               rtnl_lock();
-                               /* Follow me in net/core/wireless.c */
-                               ret = wireless_process_ioctl(&ifr, cmd);
-                               rtnl_unlock();
-                               if (IW_IS_GET(cmd) &&
-                                   copy_to_user(arg, &ifr,
-                                                sizeof(struct ifreq)))
-                                       ret = -EFAULT;
-                               return ret;
-                       }
-#endif /* CONFIG_WIRELESS_EXT */
+                       if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
+                               return wext_handle_ioctl(&ifr, cmd, arg);
                        return -EINVAL;
        }
 }
@@ -3023,10 +3069,9 @@ int register_netdevice(struct net_device *dev)
 
        spin_lock_init(&dev->queue_lock);
        spin_lock_init(&dev->_xmit_lock);
+       netdev_set_lockdep_class(&dev->_xmit_lock, dev->type);
        dev->xmit_lock_owner = -1;
-#ifdef CONFIG_NET_CLS_ACT
        spin_lock_init(&dev->ingress_lock);
-#endif
 
        dev->iflink = -1;
 
@@ -3110,11 +3155,9 @@ int register_netdevice(struct net_device *dev)
 
        set_bit(__LINK_STATE_PRESENT, &dev->state);
 
-       dev->next = NULL;
        dev_init_scheduler(dev);
        write_lock_bh(&dev_base_lock);
-       *dev_tail = dev;
-       dev_tail = &dev->next;
+       list_add_tail(&dev->dev_list, &dev_base_head);
        hlist_add_head(&dev->name_hlist, head);
        hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex));
        dev_hold(dev);
@@ -3138,7 +3181,7 @@ out:
  *     chain. 0 is returned on success. A negative errno code is returned
  *     on a failure to set up the device, or if the name is a duplicate.
  *
- *     This is a wrapper around register_netdev that takes the rtnl semaphore
+ *     This is a wrapper around register_netdevice that takes the rtnl semaphore
  *     and expands the device name if you passed a format string to
  *     alloc_netdev.
  */
@@ -3271,7 +3314,6 @@ void netdev_run_todo(void)
                        continue;
                }
 
-               netdev_unregister_sysfs(dev);
                dev->reg_state = NETREG_UNREGISTERED;
 
                netdev_wait_allrefs(dev);
@@ -3282,22 +3324,20 @@ void netdev_run_todo(void)
                BUG_TRAP(!dev->ip6_ptr);
                BUG_TRAP(!dev->dn_ptr);
 
-               /* It must be the very last action,
-                * after this 'dev' may point to freed up memory.
-                */
                if (dev->destructor)
                        dev->destructor(dev);
+
+               /* Free network device */
+               kobject_put(&dev->dev.kobj);
        }
 
 out:
        mutex_unlock(&net_todo_run_mutex);
 }
 
-static struct net_device_stats *maybe_internal_stats(struct net_device *dev)
+static struct net_device_stats *internal_stats(struct net_device *dev)
 {
-       if (dev->features & NETIF_F_INTERNAL_STATS)
-               return &dev->stats;
-       return NULL;
+       return &dev->stats;
 }
 
 /**
@@ -3335,7 +3375,7 @@ struct net_device *alloc_netdev(int sizeof_priv, const char *name,
        if (sizeof_priv)
                dev->priv = netdev_priv(dev);
 
-       dev->get_stats = maybe_internal_stats;
+       dev->get_stats = internal_stats;
        setup(dev);
        strcpy(dev->name, name);
        return dev;
@@ -3390,8 +3430,6 @@ void synchronize_net(void)
 
 void unregister_netdevice(struct net_device *dev)
 {
-       struct net_device *d, **dp;
-
        BUG_ON(dev_boot_phase);
        ASSERT_RTNL();
 
@@ -3411,19 +3449,11 @@ void unregister_netdevice(struct net_device *dev)
                dev_close(dev);
 
        /* And unlink it from device chain. */
-       for (dp = &dev_base; (d = *dp) != NULL; dp = &d->next) {
-               if (d == dev) {
-                       write_lock_bh(&dev_base_lock);
-                       hlist_del(&dev->name_hlist);
-                       hlist_del(&dev->index_hlist);
-                       if (dev_tail == &dev->next)
-                               dev_tail = dp;
-                       *dp = d->next;
-                       write_unlock_bh(&dev_base_lock);
-                       break;
-               }
-       }
-       BUG_ON(!d);
+       write_lock_bh(&dev_base_lock);
+       list_del(&dev->dev_list);
+       hlist_del(&dev->name_hlist);
+       hlist_del(&dev->index_hlist);
+       write_unlock_bh(&dev_base_lock);
 
        dev->reg_state = NETREG_UNREGISTERING;
 
@@ -3449,6 +3479,9 @@ void unregister_netdevice(struct net_device *dev)
        /* Notifier chain MUST detach us from master device. */
        BUG_TRAP(!dev->master);
 
+       /* Remove entries from sysfs */
+       netdev_unregister_sysfs(dev);
+
        /* Finish processing unregister after unlock */
        net_set_todo(dev);
 
@@ -3488,7 +3521,7 @@ static int dev_cpu_callback(struct notifier_block *nfb,
        unsigned int cpu, oldcpu = (unsigned long)ocpu;
        struct softnet_data *sd, *oldsd;
 
-       if (action != CPU_DEAD)
+       if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
                return NOTIFY_OK;
 
        local_irq_disable();