diff options
Diffstat (limited to 'drivers/net/bonding/bond_main.c')
-rw-r--r-- | drivers/net/bonding/bond_main.c | 280 |
1 files changed, 85 insertions, 195 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 16d6fe9..22bd03b 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -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; + u8 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(); |