]> rtime.felk.cvut.cz Git - lisovros/linux_canprio.git/blobdiff - drivers/net/bonding/bond_main.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[lisovros/linux_canprio.git] / drivers / net / bonding / bond_main.c
index c3d98dde2f86a5812067b2ef8ee52659b66dcda5..822808810a1394a1a2b14a6da2dd08fa1ec3f307 100644 (file)
@@ -90,6 +90,7 @@
 #define BOND_LINK_ARP_INTERV   0
 
 static int max_bonds   = BOND_DEFAULT_MAX_BONDS;
+static int tx_queues   = BOND_DEFAULT_TX_QUEUES;
 static int num_grat_arp = 1;
 static int num_unsol_na = 1;
 static int miimon      = BOND_LINK_MON_INTERV;
@@ -106,10 +107,13 @@ static int arp_interval = BOND_LINK_ARP_INTERV;
 static char *arp_ip_target[BOND_MAX_ARP_TARGETS];
 static char *arp_validate;
 static char *fail_over_mac;
+static int all_slaves_active = 0;
 static struct bond_params bonding_defaults;
 
 module_param(max_bonds, int, 0);
 MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
+module_param(tx_queues, int, 0);
+MODULE_PARM_DESC(tx_queues, "Max number of transmit queues (default = 16)");
 module_param(num_grat_arp, int, 0644);
 MODULE_PARM_DESC(num_grat_arp, "Number of gratuitous ARP packets to send on failover event");
 module_param(num_unsol_na, int, 0644);
@@ -155,6 +159,10 @@ module_param(arp_validate, charp, 0);
 MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes: none (default), active, backup or all");
 module_param(fail_over_mac, charp, 0);
 MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to the same MAC.  none (default), active or follow");
+module_param(all_slaves_active, int, 0);
+MODULE_PARM_DESC(all_slaves_active, "Keep all frames received on an interface"
+                                    "by setting active flag for all slaves.  "
+                                    "0 for never (default), 1 for always.");
 
 /*----------------------------- Global variables ----------------------------*/
 
@@ -1522,16 +1530,32 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                }
        }
 
+       /* If this is the first slave, then we need to set the master's hardware
+        * address to be the same as the slave's. */
+       if (bond->slave_cnt == 0)
+               memcpy(bond->dev->dev_addr, slave_dev->dev_addr,
+                      slave_dev->addr_len);
+
+
        new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
        if (!new_slave) {
                res = -ENOMEM;
                goto err_undo_flags;
        }
 
-       /* save slave's original flags before calling
-        * netdev_set_master and dev_open
+       /*
+        * Set the new_slave's queue_id to be zero.  Queue ID mapping
+        * is set via sysfs or module option if desired.
         */
-       new_slave->original_flags = slave_dev->flags;
+       new_slave->queue_id = 0;
+
+       /* Save slave's original mtu and then set it to match the bond */
+       new_slave->original_mtu = slave_dev->mtu;
+       res = dev_set_mtu(slave_dev, bond->dev->mtu);
+       if (res) {
+               pr_debug("Error %d calling dev_set_mtu\n", res);
+               goto err_free;
+       }
 
        /*
         * Save slave's original ("permanent") mac address for modes
@@ -1550,7 +1574,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                res = dev_set_mac_address(slave_dev, &addr);
                if (res) {
                        pr_debug("Error %d calling set_mac_address\n", res);
-                       goto err_free;
+                       goto err_restore_mtu;
                }
        }
 
@@ -1793,6 +1817,9 @@ err_restore_mac:
                dev_set_mac_address(slave_dev, &addr);
        }
 
+err_restore_mtu:
+       dev_set_mtu(slave_dev, new_slave->original_mtu);
+
 err_free:
        kfree(new_slave);
 
@@ -1980,6 +2007,8 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
                dev_set_mac_address(slave_dev, &addr);
        }
 
+       dev_set_mtu(slave_dev, slave->original_mtu);
+
        slave_dev->priv_flags &= ~(IFF_MASTER_8023AD | IFF_MASTER_ALB |
                                   IFF_SLAVE_INACTIVE | IFF_BONDING |
                                   IFF_SLAVE_NEEDARP);
@@ -2566,7 +2595,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
                /*
                 * This target is not on a VLAN
                 */
-               if (rt->u.dst.dev == bond->dev) {
+               if (rt->dst.dev == bond->dev) {
                        ip_rt_put(rt);
                        pr_debug("basa: rtdev == bond->dev: arp_send\n");
                        bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
@@ -2577,7 +2606,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
                vlan_id = 0;
                list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
                        vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
-                       if (vlan_dev == rt->u.dst.dev) {
+                       if (vlan_dev == rt->dst.dev) {
                                vlan_id = vlan->vlan_id;
                                pr_debug("basa: vlan match on %s %d\n",
                                       vlan_dev->name, vlan_id);
@@ -2595,7 +2624,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
                if (net_ratelimit()) {
                        pr_warning("%s: no path to arp_ip_target %pI4 via rt.dev %s\n",
                                   bond->dev->name, &fl.fl4_dst,
-                                  rt->u.dst.dev ? rt->u.dst.dev->name : "NULL");
+                                  rt->dst.dev ? rt->dst.dev->name : "NULL");
                }
                ip_rt_put(rt);
        }
@@ -3276,6 +3305,7 @@ static void bond_info_show_slave(struct seq_file *seq,
                else
                        seq_puts(seq, "Aggregator ID: N/A\n");
        }
+       seq_printf(seq, "Slave queue ID: %d\n", slave->queue_id);
 }
 
 static int bond_info_seq_show(struct seq_file *seq, void *v)
@@ -3785,50 +3815,49 @@ static int bond_close(struct net_device *bond_dev)
        return 0;
 }
 
-static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
+static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev,
+                                               struct rtnl_link_stats64 *stats)
 {
        struct bonding *bond = netdev_priv(bond_dev);
-       struct net_device_stats *stats = &bond_dev->stats;
-       struct net_device_stats local_stats;
+       struct rtnl_link_stats64 temp;
        struct slave *slave;
        int i;
 
-       memset(&local_stats, 0, sizeof(struct net_device_stats));
+       memset(stats, 0, sizeof(*stats));
 
        read_lock_bh(&bond->lock);
 
        bond_for_each_slave(bond, slave, i) {
-               const struct net_device_stats *sstats = dev_get_stats(slave->dev);
+               const struct rtnl_link_stats64 *sstats =
+                       dev_get_stats(slave->dev, &temp);
 
-               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->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.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->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.multicast += sstats->multicast;
-               local_stats.collisions += sstats->collisions;
+               stats->multicast += sstats->multicast;
+               stats->collisions += sstats->collisions;
 
-               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->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.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;
+               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;
        }
 
-       memcpy(stats, &local_stats, sizeof(struct net_device_stats));
-
        read_unlock_bh(&bond->lock);
 
        return stats;
@@ -4412,9 +4441,59 @@ static void bond_set_xmit_hash_policy(struct bonding *bond)
        }
 }
 
+/*
+ * Lookup the slave that corresponds to a qid
+ */
+static inline int bond_slave_override(struct bonding *bond,
+                                     struct sk_buff *skb)
+{
+       int i, res = 1;
+       struct slave *slave = NULL;
+       struct slave *check_slave;
+
+       read_lock(&bond->lock);
+
+       if (!BOND_IS_OK(bond) || !skb->queue_mapping)
+               goto out;
+
+       /* Find out if any slaves have the same mapping as this skb. */
+       bond_for_each_slave(bond, check_slave, i) {
+               if (check_slave->queue_id == skb->queue_mapping) {
+                       slave = check_slave;
+                       break;
+               }
+       }
+
+       /* If the slave isn't UP, use default transmit policy. */
+       if (slave && slave->queue_id && IS_UP(slave->dev) &&
+           (slave->link == BOND_LINK_UP)) {
+               res = bond_dev_queue_xmit(bond, skb, slave->dev);
+       }
+
+out:
+       read_unlock(&bond->lock);
+       return res;
+}
+
+static u16 bond_select_queue(struct net_device *dev, struct sk_buff *skb)
+{
+       /*
+        * This helper function exists to help dev_pick_tx get the correct
+        * destination queue.  Using a helper function skips the a call to
+        * skb_tx_hash and will put the skbs in the queue we expect on their
+        * way down to the bonding driver.
+        */
+       return skb->queue_mapping;
+}
+
 static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       const struct bonding *bond = netdev_priv(dev);
+       struct bonding *bond = netdev_priv(dev);
+
+       if (TX_QUEUE_OVERRIDE(bond->params.mode)) {
+               if (!bond_slave_override(bond, skb))
+                       return NETDEV_TX_OK;
+       }
 
        switch (bond->params.mode) {
        case BOND_MODE_ROUNDROBIN:
@@ -4499,7 +4578,8 @@ static const struct net_device_ops bond_netdev_ops = {
        .ndo_open               = bond_open,
        .ndo_stop               = bond_close,
        .ndo_start_xmit         = bond_start_xmit,
-       .ndo_get_stats          = bond_get_stats,
+       .ndo_select_queue       = bond_select_queue,
+       .ndo_get_stats64        = bond_get_stats,
        .ndo_do_ioctl           = bond_do_ioctl,
        .ndo_set_multicast_list = bond_set_multicast_list,
        .ndo_change_mtu         = bond_change_mtu,
@@ -4767,6 +4847,20 @@ static int bond_check_params(struct bond_params *params)
                }
        }
 
+       if (tx_queues < 1 || tx_queues > 255) {
+               pr_warning("Warning: tx_queues (%d) should be between "
+                          "1 and 255, resetting to %d\n",
+                          tx_queues, BOND_DEFAULT_TX_QUEUES);
+               tx_queues = BOND_DEFAULT_TX_QUEUES;
+       }
+
+       if ((all_slaves_active != 0) && (all_slaves_active != 1)) {
+               pr_warning("Warning: all_slaves_active module parameter (%d), "
+                          "not of valid value (0/1), so it was set to "
+                          "0\n", all_slaves_active);
+               all_slaves_active = 0;
+       }
+
        /* reset values for TLB/ALB */
        if ((bond_mode == BOND_MODE_TLB) ||
            (bond_mode == BOND_MODE_ALB)) {
@@ -4937,6 +5031,8 @@ static int bond_check_params(struct bond_params *params)
        params->primary[0] = 0;
        params->primary_reselect = primary_reselect_value;
        params->fail_over_mac = fail_over_mac_value;
+       params->tx_queues = tx_queues;
+       params->all_slaves_active = all_slaves_active;
 
        if (primary) {
                strncpy(params->primary, primary, IFNAMSIZ);
@@ -5023,8 +5119,8 @@ int bond_create(struct net *net, const char *name)
 
        rtnl_lock();
 
-       bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "",
-                               bond_setup);
+       bond_dev = alloc_netdev_mq(sizeof(struct bonding), name ? name : "",
+                               bond_setup, tx_queues);
        if (!bond_dev) {
                pr_err("%s: eek! can't alloc netdev!\n", name);
                rtnl_unlock();