ipv4, ipv6, bonding: Restore control over number of peer notifications
[pandora-kernel.git] / drivers / net / bonding / bond_main.c
index 16d6fe9..22bd03b 100644 (file)
@@ -89,8 +89,7 @@
 
 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 num_peer_notif = 1;
 static int miimon      = BOND_LINK_MON_INTERV;
 static int updelay;
 static int downdelay;
@@ -113,10 +112,10 @@ 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);
-MODULE_PARM_DESC(num_unsol_na, "Number of unsolicited IPv6 Neighbor Advertisements packets to send on failover event");
+module_param_named(num_grat_arp, num_peer_notif, int, 0644);
+MODULE_PARM_DESC(num_grat_arp, "Number of peer notifications to send on failover event (alias of num_unsol_na)");
+module_param_named(num_unsol_na, num_peer_notif, int, 0644);
+MODULE_PARM_DESC(num_unsol_na, "Number of peer notifications to send on failover event (alias of num_grat_arp)");
 module_param(miimon, int, 0);
 MODULE_PARM_DESC(miimon, "Link check interval in milliseconds");
 module_param(updelay, int, 0);
@@ -234,7 +233,6 @@ struct bond_parm_tbl ad_select_tbl[] = {
 
 /*-------------------------- Forward declarations ---------------------------*/
 
-static void bond_send_gratuitous_arp(struct bonding *bond);
 static int bond_init(struct net_device *bond_dev);
 static void bond_uninit(struct net_device *bond_dev);
 
@@ -631,7 +629,8 @@ down:
 static int bond_update_speed_duplex(struct slave *slave)
 {
        struct net_device *slave_dev = slave->dev;
-       struct ethtool_cmd etool;
+       struct ethtool_cmd etool = { .cmd = ETHTOOL_GSET };
+       u32 slave_speed;
        int res;
 
        /* Fake speed and duplex */
@@ -645,7 +644,8 @@ static int bond_update_speed_duplex(struct slave *slave)
        if (res < 0)
                return -1;
 
-       switch (etool.speed) {
+       slave_speed = ethtool_cmd_speed(&etool);
+       switch (slave_speed) {
        case SPEED_10:
        case SPEED_100:
        case SPEED_1000:
@@ -663,7 +663,7 @@ static int bond_update_speed_duplex(struct slave *slave)
                return -1;
        }
 
-       slave->speed = etool.speed;
+       slave->speed = slave_speed;
        slave->duplex = etool.duplex;
 
        return 0;
@@ -1087,6 +1087,21 @@ static struct slave *bond_find_best_slave(struct bonding *bond)
        return bestslave;
 }
 
+static bool bond_should_notify_peers(struct bonding *bond)
+{
+       struct slave *slave = bond->curr_active_slave;
+
+       pr_debug("bond_should_notify_peers: bond %s slave %s\n",
+                bond->dev->name, slave ? slave->dev->name : "NULL");
+
+       if (!slave || !bond->send_peer_notif ||
+           test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
+               return false;
+
+       bond->send_peer_notif--;
+       return true;
+}
+
 /**
  * change_active_interface - change the active slave into the specified one
  * @bond: our bonding struct
@@ -1154,6 +1169,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                        bond_set_slave_inactive_flags(old_active);
 
                if (new_active) {
+                       bool should_notify_peers = false;
+
                        bond_set_slave_active_flags(new_active);
 
                        if (bond->params.fail_over_mac)
@@ -1161,17 +1178,19 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                                                      old_active);
 
                        if (netif_running(bond->dev)) {
-                               bond->send_grat_arp = bond->params.num_grat_arp;
-                               bond_send_gratuitous_arp(bond);
-
-                               bond->send_unsol_na = bond->params.num_unsol_na;
-                               bond_send_unsolicited_na(bond);
+                               bond->send_peer_notif =
+                                       bond->params.num_peer_notif;
+                               should_notify_peers =
+                                       bond_should_notify_peers(bond);
                        }
 
                        write_unlock_bh(&bond->curr_slave_lock);
                        read_unlock(&bond->lock);
 
                        netdev_bonding_change(bond->dev, NETDEV_BONDING_FAILOVER);
+                       if (should_notify_peers)
+                               netdev_bonding_change(bond->dev,
+                                                     NETDEV_NOTIFY_PEERS);
 
                        read_lock(&bond->lock);
                        write_lock_bh(&bond->curr_slave_lock);
@@ -1407,7 +1426,7 @@ static int bond_compute_features(struct bonding *bond)
        int i;
 
        features &= ~(NETIF_F_ALL_CSUM | BOND_VLAN_FEATURES);
-       features |=  NETIF_F_GSO_MASK | NETIF_F_NO_CSUM;
+       features |=  NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_NOCACHE_COPY;
 
        if (!bond->first_slave)
                goto done;
@@ -1452,27 +1471,17 @@ static void bond_setup_by_slave(struct net_device *bond_dev,
 }
 
 /* On bonding slaves other than the currently active slave, suppress
- * duplicates except for 802.3ad ETH_P_SLOW, alb non-mcast/bcast, and
- * ARP on active-backup slaves with arp_validate enabled.
+ * duplicates except for alb non-mcast/bcast.
  */
 static bool bond_should_deliver_exact_match(struct sk_buff *skb,
                                            struct slave *slave,
                                            struct bonding *bond)
 {
        if (bond_is_slave_inactive(slave)) {
-               if (slave_do_arp_validate(bond, slave) &&
-                   skb->protocol == __cpu_to_be16(ETH_P_ARP))
-                       return false;
-
                if (bond->params.mode == BOND_MODE_ALB &&
                    skb->pkt_type != PACKET_BROADCAST &&
                    skb->pkt_type != PACKET_MULTICAST)
-                               return false;
-
-               if (bond->params.mode == BOND_MODE_8023AD &&
-                   skb->protocol == __cpu_to_be16(ETH_P_SLOW))
                        return false;
-
                return true;
        }
        return false;
@@ -1496,6 +1505,15 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
        if (bond->params.arp_interval)
                slave->dev->last_rx = jiffies;
 
+       if (bond->recv_probe) {
+               struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
+
+               if (likely(nskb)) {
+                       bond->recv_probe(nskb, bond, slave);
+                       dev_kfree_skb(nskb);
+               }
+       }
+
        if (bond_should_deliver_exact_match(skb, slave, bond)) {
                return RX_HANDLER_EXACT;
        }
@@ -2493,7 +2511,7 @@ static void bond_miimon_commit(struct bonding *bond)
 
                        bond_update_speed_duplex(slave);
 
-                       pr_info("%s: link status definitely up for interface %s, %d Mbps %s duplex.\n",
+                       pr_info("%s: link status definitely up for interface %s, %u Mbps %s duplex.\n",
                                bond->dev->name, slave->dev->name,
                                slave->speed, slave->duplex ? "full" : "half");
 
@@ -2570,6 +2588,7 @@ void bond_mii_monitor(struct work_struct *work)
 {
        struct bonding *bond = container_of(work, struct bonding,
                                            mii_work.work);
+       bool should_notify_peers = false;
 
        read_lock(&bond->lock);
        if (bond->kill_timers)
@@ -2578,17 +2597,7 @@ void bond_mii_monitor(struct work_struct *work)
        if (bond->slave_cnt == 0)
                goto re_arm;
 
-       if (bond->send_grat_arp) {
-               read_lock(&bond->curr_slave_lock);
-               bond_send_gratuitous_arp(bond);
-               read_unlock(&bond->curr_slave_lock);
-       }
-
-       if (bond->send_unsol_na) {
-               read_lock(&bond->curr_slave_lock);
-               bond_send_unsolicited_na(bond);
-               read_unlock(&bond->curr_slave_lock);
-       }
+       should_notify_peers = bond_should_notify_peers(bond);
 
        if (bond_miimon_inspect(bond)) {
                read_unlock(&bond->lock);
@@ -2608,6 +2617,12 @@ re_arm:
                                   msecs_to_jiffies(bond->params.miimon));
 out:
        read_unlock(&bond->lock);
+
+       if (should_notify_peers) {
+               rtnl_lock();
+               netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
+               rtnl_unlock();
+       }
 }
 
 static __be32 bond_glean_dev_ip(struct net_device *dev)
@@ -2751,44 +2766,6 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
        }
 }
 
-/*
- * Kick out a gratuitous ARP for an IP on the bonding master plus one
- * for each VLAN above us.
- *
- * Caller must hold curr_slave_lock for read or better
- */
-static void bond_send_gratuitous_arp(struct bonding *bond)
-{
-       struct slave *slave = bond->curr_active_slave;
-       struct vlan_entry *vlan;
-       struct net_device *vlan_dev;
-
-       pr_debug("bond_send_grat_arp: bond %s slave %s\n",
-                bond->dev->name, slave ? slave->dev->name : "NULL");
-
-       if (!slave || !bond->send_grat_arp ||
-           test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
-               return;
-
-       bond->send_grat_arp--;
-
-       if (bond->master_ip) {
-               bond_arp_send(slave->dev, ARPOP_REPLY, bond->master_ip,
-                               bond->master_ip, 0);
-       }
-
-       if (!bond->vlgrp)
-               return;
-
-       list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
-               vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
-               if (vlan->vlan_ip) {
-                       bond_arp_send(slave->dev, ARPOP_REPLY, vlan->vlan_ip,
-                                     vlan->vlan_ip, vlan->vlan_id);
-               }
-       }
-}
-
 static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 sip, __be32 tip)
 {
        int i;
@@ -2806,48 +2783,26 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
        }
 }
 
-static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
+static void bond_arp_rcv(struct sk_buff *skb, struct bonding *bond,
+                        struct slave *slave)
 {
        struct arphdr *arp;
-       struct slave *slave;
-       struct bonding *bond;
        unsigned char *arp_ptr;
        __be32 sip, tip;
 
-       if (dev->priv_flags & IFF_802_1Q_VLAN) {
-               /*
-                * When using VLANS and bonding, dev and oriv_dev may be
-                * incorrect if the physical interface supports VLAN
-                * acceleration.  With this change ARP validation now
-                * works for hosts only reachable on the VLAN interface.
-                */
-               dev = vlan_dev_real_dev(dev);
-               orig_dev = dev_get_by_index_rcu(dev_net(skb->dev),skb->skb_iif);
-       }
-
-       if (!(dev->priv_flags & IFF_BONDING) || !(dev->flags & IFF_MASTER))
-               goto out;
+       if (skb->protocol != __cpu_to_be16(ETH_P_ARP))
+               return;
 
-       bond = netdev_priv(dev);
        read_lock(&bond->lock);
 
-       pr_debug("bond_arp_rcv: bond %s skb->dev %s orig_dev %s\n",
-                bond->dev->name, skb->dev ? skb->dev->name : "NULL",
-                orig_dev ? orig_dev->name : "NULL");
+       pr_debug("bond_arp_rcv: bond %s skb->dev %s\n",
+                bond->dev->name, skb->dev->name);
 
-       slave = bond_get_slave_by_dev(bond, orig_dev);
-       if (!slave || !slave_do_arp_validate(bond, slave))
-               goto out_unlock;
-
-       skb = skb_share_check(skb, GFP_ATOMIC);
-       if (!skb)
-               goto out_unlock;
-
-       if (!pskb_may_pull(skb, arp_hdr_len(dev)))
+       if (!pskb_may_pull(skb, arp_hdr_len(bond->dev)))
                goto out_unlock;
 
        arp = arp_hdr(skb);
-       if (arp->ar_hln != dev->addr_len ||
+       if (arp->ar_hln != bond->dev->addr_len ||
            skb->pkt_type == PACKET_OTHERHOST ||
            skb->pkt_type == PACKET_LOOPBACK ||
            arp->ar_hrd != htons(ARPHRD_ETHER) ||
@@ -2856,9 +2811,9 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
                goto out_unlock;
 
        arp_ptr = (unsigned char *)(arp + 1);
-       arp_ptr += dev->addr_len;
+       arp_ptr += bond->dev->addr_len;
        memcpy(&sip, arp_ptr, 4);
-       arp_ptr += 4 + dev->addr_len;
+       arp_ptr += 4 + bond->dev->addr_len;
        memcpy(&tip, arp_ptr, 4);
 
        pr_debug("bond_arp_rcv: %s %s/%d av %d sv %d sip %pI4 tip %pI4\n",
@@ -2881,9 +2836,6 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
 
 out_unlock:
        read_unlock(&bond->lock);
-out:
-       dev_kfree_skb(skb);
-       return NET_RX_SUCCESS;
 }
 
 /*
@@ -3243,6 +3195,7 @@ void bond_activebackup_arp_mon(struct work_struct *work)
 {
        struct bonding *bond = container_of(work, struct bonding,
                                            arp_work.work);
+       bool should_notify_peers = false;
        int delta_in_ticks;
 
        read_lock(&bond->lock);
@@ -3255,17 +3208,7 @@ void bond_activebackup_arp_mon(struct work_struct *work)
        if (bond->slave_cnt == 0)
                goto re_arm;
 
-       if (bond->send_grat_arp) {
-               read_lock(&bond->curr_slave_lock);
-               bond_send_gratuitous_arp(bond);
-               read_unlock(&bond->curr_slave_lock);
-       }
-
-       if (bond->send_unsol_na) {
-               read_lock(&bond->curr_slave_lock);
-               bond_send_unsolicited_na(bond);
-               read_unlock(&bond->curr_slave_lock);
-       }
+       should_notify_peers = bond_should_notify_peers(bond);
 
        if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
                read_unlock(&bond->lock);
@@ -3286,6 +3229,12 @@ re_arm:
                queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks);
 out:
        read_unlock(&bond->lock);
+
+       if (should_notify_peers) {
+               rtnl_lock();
+               netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS);
+               rtnl_unlock();
+       }
 }
 
 /*-------------------------- netdev event handling --------------------------*/
@@ -3339,8 +3288,8 @@ static int bond_slave_netdev_event(unsigned long event,
 
                        slave = bond_get_slave_by_dev(bond, slave_dev);
                        if (slave) {
-                               u16 old_speed = slave->speed;
-                               u16 old_duplex = slave->duplex;
+                               u32 old_speed = slave->speed;
+                               u old_duplex = slave->duplex;
 
                                bond_update_speed_duplex(slave);
 
@@ -3482,48 +3431,6 @@ static struct notifier_block bond_inetaddr_notifier = {
        .notifier_call = bond_inetaddr_event,
 };
 
-/*-------------------------- Packet type handling ---------------------------*/
-
-/* register to receive lacpdus on a bond */
-static void bond_register_lacpdu(struct bonding *bond)
-{
-       struct packet_type *pk_type = &(BOND_AD_INFO(bond).ad_pkt_type);
-
-       /* initialize packet type */
-       pk_type->type = PKT_TYPE_LACPDU;
-       pk_type->dev = bond->dev;
-       pk_type->func = bond_3ad_lacpdu_recv;
-
-       dev_add_pack(pk_type);
-}
-
-/* unregister to receive lacpdus on a bond */
-static void bond_unregister_lacpdu(struct bonding *bond)
-{
-       dev_remove_pack(&(BOND_AD_INFO(bond).ad_pkt_type));
-}
-
-void bond_register_arp(struct bonding *bond)
-{
-       struct packet_type *pt = &bond->arp_mon_pt;
-
-       if (pt->type)
-               return;
-
-       pt->type = htons(ETH_P_ARP);
-       pt->dev = bond->dev;
-       pt->func = bond_arp_rcv;
-       dev_add_pack(pt);
-}
-
-void bond_unregister_arp(struct bonding *bond)
-{
-       struct packet_type *pt = &bond->arp_mon_pt;
-
-       dev_remove_pack(pt);
-       pt->type = 0;
-}
-
 /*---------------------------- Hashing Policies -----------------------------*/
 
 /*
@@ -3617,14 +3524,14 @@ static int bond_open(struct net_device *bond_dev)
 
                queue_delayed_work(bond->wq, &bond->arp_work, 0);
                if (bond->params.arp_validate)
-                       bond_register_arp(bond);
+                       bond->recv_probe = bond_arp_rcv;
        }
 
        if (bond->params.mode == BOND_MODE_8023AD) {
                INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler);
                queue_delayed_work(bond->wq, &bond->ad_work, 0);
                /* register to receive LACPDUs */
-               bond_register_lacpdu(bond);
+               bond->recv_probe = bond_3ad_lacpdu_recv;
                bond_3ad_initiate_agg_selection(bond, 1);
        }
 
@@ -3635,18 +3542,9 @@ static int bond_close(struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
-               /* Unregister the receive of LACPDUs */
-               bond_unregister_lacpdu(bond);
-       }
-
-       if (bond->params.arp_validate)
-               bond_unregister_arp(bond);
-
        write_lock_bh(&bond->lock);
 
-       bond->send_grat_arp = 0;
-       bond->send_unsol_na = 0;
+       bond->send_peer_notif = 0;
 
        /* signal timers not to re-arm */
        bond->kill_timers = 1;
@@ -3682,6 +3580,7 @@ static int bond_close(struct net_device *bond_dev)
                 */
                bond_alb_deinitialize(bond);
        }
+       bond->recv_probe = NULL;
 
        return 0;
 }
@@ -4357,9 +4256,9 @@ static u16 bond_select_queue(struct net_device *dev, struct sk_buff *skb)
        u16 txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0;
 
        if (unlikely(txq >= dev->real_num_tx_queues)) {
-               do
+               do {
                        txq -= dev->real_num_tx_queues;
-               while (txq >= dev->real_num_tx_queues);
+               while (txq >= dev->real_num_tx_queues);
        }
        return txq;
 }
@@ -4724,16 +4623,10 @@ static int bond_check_params(struct bond_params *params)
                use_carrier = 1;
        }
 
-       if (num_grat_arp < 0 || num_grat_arp > 255) {
-               pr_warning("Warning: num_grat_arp (%d) not in range 0-255 so it was reset to 1\n",
-                          num_grat_arp);
-               num_grat_arp = 1;
-       }
-
-       if (num_unsol_na < 0 || num_unsol_na > 255) {
-               pr_warning("Warning: num_unsol_na (%d) not in range 0-255 so it was reset to 1\n",
-                          num_unsol_na);
-               num_unsol_na = 1;
+       if (num_peer_notif < 0 || num_peer_notif > 255) {
+               pr_warning("Warning: num_grat_arp/num_unsol_na (%d) not in range 0-255 so it was reset to 1\n",
+                          num_peer_notif);
+               num_peer_notif = 1;
        }
 
        /* reset values for 802.3ad */
@@ -4925,8 +4818,7 @@ static int bond_check_params(struct bond_params *params)
        params->mode = bond_mode;
        params->xmit_policy = xmit_hashtype;
        params->miimon = miimon;
-       params->num_grat_arp = num_grat_arp;
-       params->num_unsol_na = num_unsol_na;
+       params->num_peer_notif = num_peer_notif;
        params->arp_interval = arp_interval;
        params->arp_validate = arp_validate_value;
        params->updelay = updelay;
@@ -5121,7 +5013,6 @@ static int __init bonding_init(void)
 
        register_netdevice_notifier(&bond_netdev_notifier);
        register_inetaddr_notifier(&bond_inetaddr_notifier);
-       bond_register_ipv6_notifier();
 out:
        return res;
 err:
@@ -5136,7 +5027,6 @@ static void __exit bonding_exit(void)
 {
        unregister_netdevice_notifier(&bond_netdev_notifier);
        unregister_inetaddr_notifier(&bond_inetaddr_notifier);
-       bond_unregister_ipv6_notifier();
 
        bond_destroy_sysfs();
        bond_destroy_debugfs();