]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/bonding/bond_main.c
Merge branch 'fixes' of master.kernel.org:/pub/scm/linux/kernel/git/linville/wireless-2.6
[linux-2.6-omap-h63xx.git] / drivers / net / bonding / bond_main.c
index b0b26036266b51fc381e5e2eede8cf3de2cc270d..0942d82f7cbf721f62c364941a13b31373668c5b 100644 (file)
@@ -1464,10 +1464,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                        dev_set_allmulti(slave_dev, 1);
                }
 
+               netif_tx_lock_bh(bond_dev);
                /* upload master's mc_list to new slave */
                for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) {
                        dev_mc_add (slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
                }
+               netif_tx_unlock_bh(bond_dev);
        }
 
        if (bond->params.mode == BOND_MODE_8023AD) {
@@ -1746,7 +1748,9 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
                 * has been cleared (if our_slave == old_current),
                 * but before a new active slave is selected.
                 */
+               write_unlock_bh(&bond->lock);
                bond_alb_deinit_slave(bond, slave);
+               write_lock_bh(&bond->lock);
        }
 
        if (oldcurrent == slave) {
@@ -1819,7 +1823,9 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
                }
 
                /* flush master's mc_list from slave */
+               netif_tx_lock_bh(bond_dev);
                bond_mc_list_flush(bond_dev, slave_dev);
+               netif_tx_unlock_bh(bond_dev);
        }
 
        netdev_set_master(slave_dev, NULL);
@@ -1905,6 +1911,12 @@ static int bond_release_all(struct net_device *bond_dev)
                slave_dev = slave->dev;
                bond_detach_slave(bond, slave);
 
+               /* now that the slave is detached, unlock and perform
+                * all the undo steps that should not be called from
+                * within a lock.
+                */
+               write_unlock_bh(&bond->lock);
+
                if ((bond->params.mode == BOND_MODE_TLB) ||
                    (bond->params.mode == BOND_MODE_ALB)) {
                        /* must be called only after the slave
@@ -1915,12 +1927,6 @@ static int bond_release_all(struct net_device *bond_dev)
 
                bond_compute_features(bond);
 
-               /* now that the slave is detached, unlock and perform
-                * all the undo steps that should not be called from
-                * within a lock.
-                */
-               write_unlock_bh(&bond->lock);
-
                bond_destroy_slave_symlinks(bond_dev, slave_dev);
                bond_del_vlans_from_slave(bond, slave_dev);
 
@@ -1940,7 +1946,9 @@ static int bond_release_all(struct net_device *bond_dev)
                        }
 
                        /* flush master's mc_list from slave */
+                       netif_tx_lock_bh(bond_dev);
                        bond_mc_list_flush(bond_dev, slave_dev);
+                       netif_tx_unlock_bh(bond_dev);
                }
 
                netdev_set_master(slave_dev, NULL);
@@ -2384,7 +2392,9 @@ void bond_mii_monitor(struct work_struct *work)
                rtnl_lock();
                read_lock(&bond->lock);
                __bond_mii_monitor(bond, 1);
-               rtnl_unlock();
+               read_unlock(&bond->lock);
+               rtnl_unlock();  /* might sleep, hold no other locks */
+               read_lock(&bond->lock);
        }
 
        delay = ((bond->params.miimon * HZ) / 1000) ? : 1;
@@ -2513,7 +2523,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
                fl.fl4_dst = targets[i];
                fl.fl4_tos = RTO_ONLINK;
 
-               rv = ip_route_output_key(&rt, &fl);
+               rv = ip_route_output_key(&init_net, &rt, &fl);
                if (rv) {
                        if (net_ratelimit()) {
                                printk(KERN_WARNING DRV_NAME
@@ -2791,14 +2801,11 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
        }
 
        if (do_failover) {
-               rtnl_lock();
                write_lock_bh(&bond->curr_slave_lock);
 
                bond_select_active_slave(bond);
 
                write_unlock_bh(&bond->curr_slave_lock);
-               rtnl_unlock();
-
        }
 
 re_arm:
@@ -2855,8 +2862,6 @@ void bond_activebackup_arp_mon(struct work_struct *work)
 
                                slave->link = BOND_LINK_UP;
 
-                               rtnl_lock();
-
                                write_lock_bh(&bond->curr_slave_lock);
 
                                if ((!bond->curr_active_slave) &&
@@ -2892,7 +2897,6 @@ void bond_activebackup_arp_mon(struct work_struct *work)
                                }
 
                                write_unlock_bh(&bond->curr_slave_lock);
-                               rtnl_unlock();
                        }
                } else {
                        read_lock(&bond->curr_slave_lock);
@@ -2962,7 +2966,6 @@ void bond_activebackup_arp_mon(struct work_struct *work)
                               bond->dev->name,
                               slave->dev->name);
 
-                       rtnl_lock();
                        write_lock_bh(&bond->curr_slave_lock);
 
                        bond_select_active_slave(bond);
@@ -2970,8 +2973,6 @@ void bond_activebackup_arp_mon(struct work_struct *work)
 
                        write_unlock_bh(&bond->curr_slave_lock);
 
-                       rtnl_unlock();
-
                        bond->current_arp_slave = slave;
 
                        if (slave) {
@@ -2989,13 +2990,10 @@ void bond_activebackup_arp_mon(struct work_struct *work)
                               bond->primary_slave->dev->name);
 
                        /* primary is up so switch to it */
-                       rtnl_lock();
                        write_lock_bh(&bond->curr_slave_lock);
                        bond_change_active_slave(bond, bond->primary_slave);
                        write_unlock_bh(&bond->curr_slave_lock);
 
-                       rtnl_unlock();
-
                        slave = bond->primary_slave;
                        slave->jiffies = jiffies;
                } else {
@@ -3399,9 +3397,7 @@ static int bond_master_netdev_event(unsigned long event, struct net_device *bond
        case NETDEV_CHANGENAME:
                return bond_event_changename(event_bond);
        case NETDEV_UNREGISTER:
-               /*
-                * TODO: remove a bond from the list?
-                */
+               bond_release_all(event_bond->dev);
                break;
        default:
                break;
@@ -3767,42 +3763,45 @@ static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
 {
        struct bonding *bond = bond_dev->priv;
        struct net_device_stats *stats = &(bond->stats), *sstats;
+       struct net_device_stats local_stats;
        struct slave *slave;
        int i;
 
-       memset(stats, 0, sizeof(struct net_device_stats));
+       memset(&local_stats, 0, sizeof(struct net_device_stats));
 
        read_lock_bh(&bond->lock);
 
        bond_for_each_slave(bond, slave, i) {
                sstats = slave->dev->get_stats(slave->dev);
-               stats->rx_packets += sstats->rx_packets;
-               stats->rx_bytes += sstats->rx_bytes;
-               stats->rx_errors += sstats->rx_errors;
-               stats->rx_dropped += sstats->rx_dropped;
+               local_stats.rx_packets += sstats->rx_packets;
+               local_stats.rx_bytes += sstats->rx_bytes;
+               local_stats.rx_errors += sstats->rx_errors;
+               local_stats.rx_dropped += sstats->rx_dropped;
 
-               stats->tx_packets += sstats->tx_packets;
-               stats->tx_bytes += sstats->tx_bytes;
-               stats->tx_errors += sstats->tx_errors;
-               stats->tx_dropped += sstats->tx_dropped;
+               local_stats.tx_packets += sstats->tx_packets;
+               local_stats.tx_bytes += sstats->tx_bytes;
+               local_stats.tx_errors += sstats->tx_errors;
+               local_stats.tx_dropped += sstats->tx_dropped;
 
-               stats->multicast += sstats->multicast;
-               stats->collisions += sstats->collisions;
+               local_stats.multicast += sstats->multicast;
+               local_stats.collisions += sstats->collisions;
 
-               stats->rx_length_errors += sstats->rx_length_errors;
-               stats->rx_over_errors += sstats->rx_over_errors;
-               stats->rx_crc_errors += sstats->rx_crc_errors;
-               stats->rx_frame_errors += sstats->rx_frame_errors;
-               stats->rx_fifo_errors += sstats->rx_fifo_errors;
-               stats->rx_missed_errors += sstats->rx_missed_errors;
+               local_stats.rx_length_errors += sstats->rx_length_errors;
+               local_stats.rx_over_errors += sstats->rx_over_errors;
+               local_stats.rx_crc_errors += sstats->rx_crc_errors;
+               local_stats.rx_frame_errors += sstats->rx_frame_errors;
+               local_stats.rx_fifo_errors += sstats->rx_fifo_errors;
+               local_stats.rx_missed_errors += sstats->rx_missed_errors;
 
-               stats->tx_aborted_errors += sstats->tx_aborted_errors;
-               stats->tx_carrier_errors += sstats->tx_carrier_errors;
-               stats->tx_fifo_errors += sstats->tx_fifo_errors;
-               stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
-               stats->tx_window_errors += sstats->tx_window_errors;
+               local_stats.tx_aborted_errors += sstats->tx_aborted_errors;
+               local_stats.tx_carrier_errors += sstats->tx_carrier_errors;
+               local_stats.tx_fifo_errors += sstats->tx_fifo_errors;
+               local_stats.tx_heartbeat_errors += sstats->tx_heartbeat_errors;
+               local_stats.tx_window_errors += sstats->tx_window_errors;
        }
 
+       memcpy(stats, &local_stats, sizeof(struct net_device_stats));
+
        read_unlock_bh(&bond->lock);
 
        return stats;
@@ -3935,8 +3934,6 @@ static void bond_set_multicast_list(struct net_device *bond_dev)
        struct bonding *bond = bond_dev->priv;
        struct dev_mc_list *dmi;
 
-       write_lock_bh(&bond->lock);
-
        /*
         * Do promisc before checking multicast_mode
         */
@@ -3957,6 +3954,8 @@ static void bond_set_multicast_list(struct net_device *bond_dev)
                bond_set_allmulti(bond, -1);
        }
 
+       read_lock(&bond->lock);
+
        bond->flags = bond_dev->flags;
 
        /* looking for addresses to add to slaves' mc list */
@@ -3977,7 +3976,7 @@ static void bond_set_multicast_list(struct net_device *bond_dev)
        bond_mc_list_destroy(bond);
        bond_mc_list_copy(bond_dev->mc_list, bond, GFP_ATOMIC);
 
-       write_unlock_bh(&bond->lock);
+       read_unlock(&bond->lock);
 }
 
 /*
@@ -4524,7 +4523,9 @@ static void bond_free_all(void)
                struct net_device *bond_dev = bond->dev;
 
                bond_work_cancel_all(bond);
+               netif_tx_lock_bh(bond_dev);
                bond_mc_list_destroy(bond);
+               netif_tx_unlock_bh(bond_dev);
                /* Release the bonded slaves */
                bond_release_all(bond_dev);
                bond_deinit(bond_dev);
@@ -4540,18 +4541,32 @@ static void bond_free_all(void)
 
 /*
  * Convert string input module parms.  Accept either the
- * number of the mode or its string name.
+ * number of the mode or its string name.  A bit complicated because
+ * some mode names are substrings of other names, and calls from sysfs
+ * may have whitespace in the name (trailing newlines, for example).
  */
-int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl)
+int bond_parse_parm(const char *buf, struct bond_parm_tbl *tbl)
 {
-       int i;
+       int mode = -1, i, rv;
+       char *p, modestr[BOND_MAX_MODENAME_LEN + 1] = { 0, };
+
+       for (p = (char *)buf; *p; p++)
+               if (!(isdigit(*p) || isspace(*p)))
+                       break;
+
+       if (*p)
+               rv = sscanf(buf, "%20s", modestr);
+       else
+               rv = sscanf(buf, "%d", &mode);
+
+       if (!rv)
+               return -1;
 
        for (i = 0; tbl[i].modename; i++) {
-               if ((isdigit(*mode_arg) &&
-                    tbl[i].mode == simple_strtol(mode_arg, NULL, 0)) ||
-                   (strcmp(mode_arg, tbl[i].modename) == 0)) {
+               if (mode == tbl[i].mode)
+                       return tbl[i].mode;
+               if (strcmp(modestr, tbl[i].modename) == 0)
                        return tbl[i].mode;
-               }
        }
 
        return -1;
@@ -4865,9 +4880,24 @@ static struct lock_class_key bonding_netdev_xmit_lock_key;
 int bond_create(char *name, struct bond_params *params, struct bonding **newbond)
 {
        struct net_device *bond_dev;
+       struct bonding *bond, *nxt;
        int res;
 
        rtnl_lock();
+       down_write(&bonding_rwsem);
+
+       /* Check to see if the bond already exists. */
+       if (name) {
+               list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list)
+                       if (strnicmp(bond->dev->name, name, IFNAMSIZ) == 0) {
+                               printk(KERN_ERR DRV_NAME
+                              ": cannot add bond %s; it already exists\n",
+                                      name);
+                               res = -EPERM;
+                               goto out_rtnl;
+                       }
+       }
+
        bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "",
                                ether_setup);
        if (!bond_dev) {
@@ -4906,10 +4936,12 @@ int bond_create(char *name, struct bond_params *params, struct bonding **newbond
 
        netif_carrier_off(bond_dev);
 
+       up_write(&bonding_rwsem);
        rtnl_unlock(); /* allows sysfs registration of net device */
        res = bond_create_sysfs_entry(bond_dev->priv);
        if (res < 0) {
                rtnl_lock();
+               down_write(&bonding_rwsem);
                goto out_bond;
        }
 
@@ -4920,6 +4952,7 @@ out_bond:
 out_netdev:
        free_netdev(bond_dev);
 out_rtnl:
+       up_write(&bonding_rwsem);
        rtnl_unlock();
        return res;
 }
@@ -4940,6 +4973,9 @@ static int __init bonding_init(void)
 #ifdef CONFIG_PROC_FS
        bond_create_proc_dir();
 #endif
+
+       init_rwsem(&bonding_rwsem);
+
        for (i = 0; i < max_bonds; i++) {
                res = bond_create(NULL, &bonding_defaults, NULL);
                if (res)