diff options
Diffstat (limited to 'drivers/net/bonding/bond_main.c')
-rw-r--r-- | drivers/net/bonding/bond_main.c | 485 |
1 files changed, 197 insertions, 288 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index f1b566f..9145834c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -99,6 +99,7 @@ static char *mode; static char *primary; static char *primary_reselect; static char *lacp_rate; +static int min_links; static char *ad_select; static char *xmit_hash_policy; static int arp_interval = BOND_LINK_ARP_INTERV; @@ -151,6 +152,9 @@ module_param(ad_select, charp, 0); MODULE_PARM_DESC(ad_select, "803.ad aggregation selection logic; " "0 for stable (default), 1 for bandwidth, " "2 for count"); +module_param(min_links, int, 0); +MODULE_PARM_DESC(min_links, "Minimum number of available links before turning on carrier"); + module_param(xmit_hash_policy, charp, 0); MODULE_PARM_DESC(xmit_hash_policy, "balance-xor and 802.3ad hashing method; " "0 for layer 2 (default), 1 for layer 3+4, " @@ -330,16 +334,6 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id) kfree(vlan); - if (list_empty(&bond->vlan_list) && - (bond->slave_cnt == 0)) { - /* Last VLAN removed and no slaves, so - * restore block on adding VLANs. This will - * be removed once new slaves that are not - * VLAN challenged will be added. - */ - bond->dev->features |= NETIF_F_VLAN_CHALLENGED; - } - res = 0; goto out; } @@ -400,7 +394,6 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev) { skb->dev = slave_dev; - skb->priority = 1; BUILD_BUG_ON(sizeof(skb->queue_mapping) != sizeof(qdisc_skb_cb(skb)->bond_queue_mapping)); @@ -415,9 +408,8 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, } /* - * In the following 3 functions, bond_vlan_rx_register(), bond_vlan_rx_add_vid - * and bond_vlan_rx_kill_vid, We don't protect the slave list iteration with a - * lock because: + * In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid, + * We don't protect the slave list iteration with a lock because: * a. This operation is performed in IOCTL context, * b. The operation is protected by the RTNL semaphore in the 8021q code, * c. Holding a lock with BH disabled while directly calling a base driver @@ -433,33 +425,6 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, */ /** - * bond_vlan_rx_register - Propagates registration to slaves - * @bond_dev: bonding net device that got called - * @grp: vlan group being registered - */ -static void bond_vlan_rx_register(struct net_device *bond_dev, - struct vlan_group *grp) -{ - struct bonding *bond = netdev_priv(bond_dev); - struct slave *slave; - int i; - - write_lock_bh(&bond->lock); - bond->vlgrp = grp; - write_unlock_bh(&bond->lock); - - bond_for_each_slave(bond, slave, i) { - struct net_device *slave_dev = slave->dev; - const struct net_device_ops *slave_ops = slave_dev->netdev_ops; - - if ((slave_dev->features & NETIF_F_HW_VLAN_RX) && - slave_ops->ndo_vlan_rx_register) { - slave_ops->ndo_vlan_rx_register(slave_dev, grp); - } - } -} - -/** * bond_vlan_rx_add_vid - Propagates adding an id to slaves * @bond_dev: bonding net device that got called * @vid: vlan id being added @@ -496,7 +461,6 @@ static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid) { struct bonding *bond = netdev_priv(bond_dev); struct slave *slave; - struct net_device *vlan_dev; int i, res; bond_for_each_slave(bond, slave, i) { @@ -505,12 +469,7 @@ static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid) if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) && slave_ops->ndo_vlan_rx_kill_vid) { - /* Save and then restore vlan_dev in the grp array, - * since the slave's driver might clear it. - */ - vlan_dev = vlan_group_get_device(bond->vlgrp, vid); slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vid); - vlan_group_set_device(bond->vlgrp, vid, vlan_dev); } } @@ -526,13 +485,6 @@ static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *sla struct vlan_entry *vlan; const struct net_device_ops *slave_ops = slave_dev->netdev_ops; - if (!bond->vlgrp) - return; - - if ((slave_dev->features & NETIF_F_HW_VLAN_RX) && - slave_ops->ndo_vlan_rx_register) - slave_ops->ndo_vlan_rx_register(slave_dev, bond->vlgrp); - if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) || !(slave_ops->ndo_vlan_rx_add_vid)) return; @@ -546,30 +498,16 @@ static void bond_del_vlans_from_slave(struct bonding *bond, { const struct net_device_ops *slave_ops = slave_dev->netdev_ops; struct vlan_entry *vlan; - struct net_device *vlan_dev; - - if (!bond->vlgrp) - return; if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) || !(slave_ops->ndo_vlan_rx_kill_vid)) - goto unreg; + return; list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { if (!vlan->vlan_id) continue; - /* Save and then restore vlan_dev in the grp array, - * since the slave's driver might clear it. - */ - vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id); slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vlan->vlan_id); - vlan_group_set_device(bond->vlgrp, vlan->vlan_id, vlan_dev); } - -unreg: - if ((slave_dev->features & NETIF_F_HW_VLAN_RX) && - slave_ops->ndo_vlan_rx_register) - slave_ops->ndo_vlan_rx_register(slave_dev, NULL); } /*------------------------------- Link status -------------------------------*/ @@ -613,39 +551,28 @@ down: /* * Get link speed and duplex from the slave's base driver * using ethtool. If for some reason the call fails or the - * values are invalid, fake speed and duplex to 100/Full + * values are invalid, set speed and duplex to -1, * and return error. */ static int bond_update_speed_duplex(struct slave *slave) { struct net_device *slave_dev = slave->dev; - struct ethtool_cmd etool = { .cmd = ETHTOOL_GSET }; + struct ethtool_cmd ecmd; u32 slave_speed; int res; - /* Fake speed and duplex */ - slave->speed = SPEED_100; - slave->duplex = DUPLEX_FULL; - - if (!slave_dev->ethtool_ops || !slave_dev->ethtool_ops->get_settings) - return -1; + slave->speed = SPEED_UNKNOWN; + slave->duplex = DUPLEX_UNKNOWN; - res = slave_dev->ethtool_ops->get_settings(slave_dev, &etool); + res = __ethtool_get_settings(slave_dev, &ecmd); if (res < 0) return -1; - slave_speed = ethtool_cmd_speed(&etool); - switch (slave_speed) { - case SPEED_10: - case SPEED_100: - case SPEED_1000: - case SPEED_10000: - break; - default: + slave_speed = ethtool_cmd_speed(&ecmd); + if (slave_speed == 0 || slave_speed == ((__u32) -1)) return -1; - } - switch (etool.duplex) { + switch (ecmd.duplex) { case DUPLEX_FULL: case DUPLEX_HALF: break; @@ -654,7 +581,7 @@ static int bond_update_speed_duplex(struct slave *slave) } slave->speed = slave_speed; - slave->duplex = etool.duplex; + slave->duplex = ecmd.duplex; return 0; } @@ -850,13 +777,13 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) __bond_resend_igmp_join_requests(bond->dev); /* rejoin all groups on vlan devices */ - if (bond->vlgrp) { - list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - vlan_dev = vlan_group_get_device(bond->vlgrp, - vlan->vlan_id); - if (vlan_dev) - __bond_resend_igmp_join_requests(vlan_dev); - } + list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { + rcu_read_lock(); + vlan_dev = __vlan_find_dev_deep(bond->dev, + vlan->vlan_id); + rcu_read_unlock(); + if (vlan_dev) + __bond_resend_igmp_join_requests(vlan_dev); } if (--bond->igmp_retrans > 0) @@ -934,6 +861,23 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, } } +static struct slave *bond_get_old_active(struct bonding *bond, + struct slave *new_active) +{ + struct slave *slave; + int i; + + bond_for_each_slave(bond, slave, i) { + if (slave == new_active) + continue; + + if (!compare_ether_addr(bond->dev->dev_addr, slave->dev->dev_addr)) + return slave; + } + + return NULL; +} + /* * bond_do_fail_over_mac * @@ -971,6 +915,9 @@ static void bond_do_fail_over_mac(struct bonding *bond, write_unlock_bh(&bond->curr_slave_lock); read_unlock(&bond->lock); + if (!old_active) + old_active = bond_get_old_active(bond, new_active); + if (old_active) { memcpy(tmp_mac, new_active->dev->dev_addr, ETH_ALEN); memcpy(saddr.sa_data, old_active->dev->dev_addr, @@ -1581,7 +1528,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* no need to lock since we're protected by rtnl_lock */ if (slave_dev->features & NETIF_F_VLAN_CHALLENGED) { pr_debug("%s: NETIF_F_VLAN_CHALLENGED\n", slave_dev->name); - if (bond->vlgrp) { + if (bond_vlan_used(bond)) { pr_err("%s: Error: cannot enslave VLAN challenged slave %s on VLAN enabled bond %s\n", bond_dev->name, slave_dev->name, bond_dev->name); return -EPERM; @@ -1788,6 +1735,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) bond_compute_features(bond); + bond_update_speed_duplex(new_slave); + read_lock(&bond->lock); new_slave->last_arp_rx = jiffies; @@ -1831,17 +1780,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) new_slave->link = BOND_LINK_DOWN; } - if (bond_update_speed_duplex(new_slave) && - (new_slave->link != BOND_LINK_DOWN)) { - pr_warning("%s: Warning: failed to get speed and duplex from %s, assumed to be 100Mb/sec and Full.\n", - bond_dev->name, new_slave->dev->name); - - if (bond->params.mode == BOND_MODE_8023AD) { - pr_warning("%s: Warning: Operation of 802.3ad mode requires ETHTOOL support in base driver for proper aggregator selection.\n", - bond_dev->name); - } - } - if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) { /* if there is a primary slave, remember it */ if (strcmp(bond->params.primary, new_slave->dev->name) == 0) { @@ -1869,8 +1807,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* Initialize AD with the number of times that the AD timer is called in 1 second * can be called only after the mac address of the bond is set */ - bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL, - bond->params.lacp_fast); + bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL); } else { SLAVE_AD_INFO(new_slave).id = SLAVE_AD_INFO(new_slave->prev).id + 1; @@ -2098,7 +2035,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) */ memset(bond_dev->dev_addr, 0, bond_dev->addr_len); - if (bond->vlgrp) { + if (bond_vlan_used(bond)) { pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n", bond_dev->name, bond_dev->name); pr_warning("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs'.\n", @@ -2286,7 +2223,7 @@ static int bond_release_all(struct net_device *bond_dev) */ memset(bond_dev->dev_addr, 0, bond_dev->addr_len); - if (bond->vlgrp) { + if (bond_vlan_used(bond)) { pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n", bond_dev->name, bond_dev->name); pr_warning("%s: When re-adding slaves, make sure the bond's HW address matches its VLANs'.\n", @@ -2527,8 +2464,6 @@ static void bond_miimon_commit(struct bonding *bond) bond_set_backup_slave(slave); } - bond_update_speed_duplex(slave); - 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"); @@ -2607,10 +2542,11 @@ void bond_mii_monitor(struct work_struct *work) struct bonding *bond = container_of(work, struct bonding, mii_work.work); bool should_notify_peers = false; + unsigned long delay; read_lock(&bond->lock); - if (bond->kill_timers) - goto out; + + delay = msecs_to_jiffies(bond->params.miimon); if (bond->slave_cnt == 0) goto re_arm; @@ -2619,7 +2555,15 @@ void bond_mii_monitor(struct work_struct *work) if (bond_miimon_inspect(bond)) { read_unlock(&bond->lock); - rtnl_lock(); + + /* Race avoidance with bond_close cancel of workqueue */ + if (!rtnl_trylock()) { + read_lock(&bond->lock); + delay = 1; + should_notify_peers = false; + goto re_arm; + } + read_lock(&bond->lock); bond_miimon_commit(bond); @@ -2631,42 +2575,22 @@ void bond_mii_monitor(struct work_struct *work) re_arm: if (bond->params.miimon) - queue_delayed_work(bond->wq, &bond->mii_work, - msecs_to_jiffies(bond->params.miimon)); -out: + queue_delayed_work(bond->wq, &bond->mii_work, delay); + read_unlock(&bond->lock); if (should_notify_peers) { - rtnl_lock(); + if (!rtnl_trylock()) { + read_lock(&bond->lock); + bond->send_peer_notif++; + read_unlock(&bond->lock); + return; + } netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS); rtnl_unlock(); } } -static __be32 bond_glean_dev_ip(struct net_device *dev) -{ - struct in_device *idev; - struct in_ifaddr *ifa; - __be32 addr = 0; - - if (!dev) - return 0; - - rcu_read_lock(); - idev = __in_dev_get_rcu(dev); - if (!idev) - goto out; - - ifa = idev->ifa_list; - if (!ifa) - goto out; - - addr = ifa->ifa_local; -out: - rcu_read_unlock(); - return addr; -} - static int bond_has_this_ip(struct bonding *bond, __be32 ip) { struct vlan_entry *vlan; @@ -2724,7 +2648,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) if (!targets[i]) break; pr_debug("basa: target %x\n", targets[i]); - if (!bond->vlgrp) { + if (!bond_vlan_used(bond)) { pr_debug("basa: empty vlan: arp_send\n"); bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i], bond->master_ip, 0); @@ -2759,7 +2683,10 @@ 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); + rcu_read_lock(); + vlan_dev = __vlan_find_dev_deep(bond->dev, + vlan->vlan_id); + rcu_read_unlock(); if (vlan_dev == rt->dst.dev) { vlan_id = vlan->vlan_id; pr_debug("basa: vlan match on %s %d\n", @@ -2876,9 +2803,6 @@ void bond_loadbalance_arp_mon(struct work_struct *work) delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); - if (bond->kill_timers) - goto out; - if (bond->slave_cnt == 0) goto re_arm; @@ -2977,7 +2901,7 @@ void bond_loadbalance_arp_mon(struct work_struct *work) re_arm: if (bond->params.arp_interval) queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks); -out: + read_unlock(&bond->lock); } @@ -3222,9 +3146,6 @@ void bond_activebackup_arp_mon(struct work_struct *work) read_lock(&bond->lock); - if (bond->kill_timers) - goto out; - delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); if (bond->slave_cnt == 0) @@ -3234,7 +3155,15 @@ void bond_activebackup_arp_mon(struct work_struct *work) if (bond_ab_arp_inspect(bond, delta_in_ticks)) { read_unlock(&bond->lock); - rtnl_lock(); + + /* Race avoidance with bond_close flush of workqueue */ + if (!rtnl_trylock()) { + read_lock(&bond->lock); + delta_in_ticks = 1; + should_notify_peers = false; + goto re_arm; + } + read_lock(&bond->lock); bond_ab_arp_commit(bond, delta_in_ticks); @@ -3249,11 +3178,16 @@ void bond_activebackup_arp_mon(struct work_struct *work) re_arm: if (bond->params.arp_interval) queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks); -out: + read_unlock(&bond->lock); if (should_notify_peers) { - rtnl_lock(); + if (!rtnl_trylock()) { + read_lock(&bond->lock); + bond->send_peer_notif++; + read_unlock(&bond->lock); + return; + } netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS); rtnl_unlock(); } @@ -3282,6 +3216,12 @@ static int bond_master_netdev_event(unsigned long event, switch (event) { case NETDEV_CHANGENAME: return bond_event_changename(event_bond); + case NETDEV_UNREGISTER: + bond_remove_proc_entry(event_bond); + break; + case NETDEV_REGISTER: + bond_create_proc_entry(event_bond); + break; default: break; } @@ -3294,6 +3234,7 @@ static int bond_slave_netdev_event(unsigned long event, { struct net_device *bond_dev = slave_dev->master; struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave = NULL; switch (event) { case NETDEV_UNREGISTER: @@ -3304,20 +3245,16 @@ static int bond_slave_netdev_event(unsigned long event, bond_release(bond_dev, slave_dev); } break; + case NETDEV_UP: case NETDEV_CHANGE: - if (bond->params.mode == BOND_MODE_8023AD || bond_is_lb(bond)) { - struct slave *slave; - - slave = bond_get_slave_by_dev(bond, slave_dev); - if (slave) { - u32 old_speed = slave->speed; - u8 old_duplex = slave->duplex; - - bond_update_speed_duplex(slave); + slave = bond_get_slave_by_dev(bond, slave_dev); + if (slave) { + u32 old_speed = slave->speed; + u8 old_duplex = slave->duplex; - if (bond_is_lb(bond)) - break; + bond_update_speed_duplex(slave); + if (bond->params.mode == BOND_MODE_8023AD) { if (old_speed != slave->speed) bond_3ad_adapter_speed_changed(slave); if (old_duplex != slave->duplex) @@ -3409,6 +3346,10 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, struct bonding *bond; struct vlan_entry *vlan; + /* we only care about primary address */ + if(ifa->ifa_flags & IFA_F_SECONDARY) + return NOTIFY_DONE; + list_for_each_entry(bond, &bn->dev_list, bond_list) { if (bond->dev == event_dev) { switch (event) { @@ -3416,7 +3357,7 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, bond->master_ip = ifa->ifa_local; return NOTIFY_OK; case NETDEV_DOWN: - bond->master_ip = bond_glean_dev_ip(bond->dev); + bond->master_ip = 0; return NOTIFY_OK; default: return NOTIFY_DONE; @@ -3424,17 +3365,15 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, } list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - if (!bond->vlgrp) - continue; - vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id); + vlan_dev = __vlan_find_dev_deep(bond->dev, + vlan->vlan_id); if (vlan_dev == event_dev) { switch (event) { case NETDEV_UP: vlan->vlan_ip = ifa->ifa_local; return NOTIFY_OK; case NETDEV_DOWN: - vlan->vlan_ip = - bond_glean_dev_ip(vlan_dev); + vlan->vlan_ip = 0; return NOTIFY_OK; default: return NOTIFY_DONE; @@ -3485,7 +3424,7 @@ static int bond_xmit_hash_policy_l34(struct sk_buff *skb, int count) int layer4_xor = 0; if (skb->protocol == htons(ETH_P_IP)) { - if (!(iph->frag_off & htons(IP_MF|IP_OFFSET)) && + if (!ip_is_fragment(iph) && (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP)) { layer4_xor = ntohs((*layer4hdr ^ *(layer4hdr + 1))); @@ -3510,47 +3449,71 @@ static int bond_xmit_hash_policy_l2(struct sk_buff *skb, int count) /*-------------------------- Device entry points ----------------------------*/ +static void bond_work_init_all(struct bonding *bond) +{ + INIT_DELAYED_WORK(&bond->mcast_work, + bond_resend_igmp_join_requests_delayed); + INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor); + INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor); + if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) + INIT_DELAYED_WORK(&bond->arp_work, bond_activebackup_arp_mon); + else + INIT_DELAYED_WORK(&bond->arp_work, bond_loadbalance_arp_mon); + INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler); +} + +static void bond_work_cancel_all(struct bonding *bond) +{ + cancel_delayed_work_sync(&bond->mii_work); + cancel_delayed_work_sync(&bond->arp_work); + cancel_delayed_work_sync(&bond->alb_work); + cancel_delayed_work_sync(&bond->ad_work); + cancel_delayed_work_sync(&bond->mcast_work); +} + static int bond_open(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave; + int i; - bond->kill_timers = 0; + /* reset slave->backup and slave->inactive */ + read_lock(&bond->lock); + if (bond->slave_cnt > 0) { + read_lock(&bond->curr_slave_lock); + bond_for_each_slave(bond, slave, i) { + if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP) + && (slave != bond->curr_active_slave)) { + bond_set_slave_inactive_flags(slave); + } else { + bond_set_slave_active_flags(slave); + } + } + read_unlock(&bond->curr_slave_lock); + } + read_unlock(&bond->lock); - INIT_DELAYED_WORK(&bond->mcast_work, bond_resend_igmp_join_requests_delayed); + bond_work_init_all(bond); if (bond_is_lb(bond)) { /* bond_alb_initialize must be called before the timer * is started. */ - if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB))) { - /* something went wrong - fail the open operation */ + if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB))) return -ENOMEM; - } - - INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor); queue_delayed_work(bond->wq, &bond->alb_work, 0); } - if (bond->params.miimon) { /* link check interval, in milliseconds. */ - INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor); + if (bond->params.miimon) /* link check interval, in milliseconds. */ queue_delayed_work(bond->wq, &bond->mii_work, 0); - } if (bond->params.arp_interval) { /* arp interval, in milliseconds. */ - if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) - INIT_DELAYED_WORK(&bond->arp_work, - bond_activebackup_arp_mon); - else - INIT_DELAYED_WORK(&bond->arp_work, - bond_loadbalance_arp_mon); - queue_delayed_work(bond->wq, &bond->arp_work, 0); if (bond->params.arp_validate) 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->recv_probe = bond_3ad_lacpdu_recv; @@ -3565,37 +3528,10 @@ static int bond_close(struct net_device *bond_dev) struct bonding *bond = netdev_priv(bond_dev); write_lock_bh(&bond->lock); - bond->send_peer_notif = 0; - - /* signal timers not to re-arm */ - bond->kill_timers = 1; - write_unlock_bh(&bond->lock); - if (bond->params.miimon) { /* link check interval, in milliseconds. */ - cancel_delayed_work(&bond->mii_work); - } - - if (bond->params.arp_interval) { /* arp interval, in milliseconds. */ - cancel_delayed_work(&bond->arp_work); - } - - switch (bond->params.mode) { - case BOND_MODE_8023AD: - cancel_delayed_work(&bond->ad_work); - break; - case BOND_MODE_TLB: - case BOND_MODE_ALB: - cancel_delayed_work(&bond->alb_work); - break; - default: - break; - } - - if (delayed_work_pending(&bond->mcast_work)) - cancel_delayed_work(&bond->mcast_work); - + bond_work_cancel_all(bond); if (bond_is_lb(bond)) { /* Must be called only after all * slaves have been released @@ -3780,44 +3716,27 @@ static bool bond_addr_in_mc_list(unsigned char *addr, return false; } -static void bond_set_multicast_list(struct net_device *bond_dev) +static void bond_change_rx_flags(struct net_device *bond_dev, int change) { struct bonding *bond = netdev_priv(bond_dev); - struct netdev_hw_addr *ha; - bool found; - - /* - * Do promisc before checking multicast_mode - */ - if ((bond_dev->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC)) - /* - * FIXME: Need to handle the error when one of the multi-slaves - * encounters error. - */ - bond_set_promiscuity(bond, 1); + if (change & IFF_PROMISC) + bond_set_promiscuity(bond, + bond_dev->flags & IFF_PROMISC ? 1 : -1); - if (!(bond_dev->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC)) - bond_set_promiscuity(bond, -1); - - - /* set allmulti flag to slaves */ - if ((bond_dev->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI)) - /* - * FIXME: Need to handle the error when one of the multi-slaves - * encounters error. - */ - bond_set_allmulti(bond, 1); - - - if (!(bond_dev->flags & IFF_ALLMULTI) && (bond->flags & IFF_ALLMULTI)) - bond_set_allmulti(bond, -1); + if (change & IFF_ALLMULTI) + bond_set_allmulti(bond, + bond_dev->flags & IFF_ALLMULTI ? 1 : -1); +} +static void bond_set_multicast_list(struct net_device *bond_dev) +{ + struct bonding *bond = netdev_priv(bond_dev); + struct netdev_hw_addr *ha; + bool found; read_lock(&bond->lock); - bond->flags = bond_dev->flags; - /* looking for addresses to add to slaves' mc list */ netdev_for_each_mc_addr(ha, bond_dev) { found = bond_addr_in_mc_list(ha->addr, &bond->mc_list, @@ -4376,12 +4295,12 @@ static const struct net_device_ops bond_netdev_ops = { .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_rx_flags = bond_change_rx_flags, + .ndo_set_rx_mode = bond_set_multicast_list, .ndo_change_mtu = bond_change_mtu, - .ndo_set_mac_address = bond_set_mac_address, + .ndo_set_mac_address = bond_set_mac_address, .ndo_neigh_setup = bond_neigh_setup, - .ndo_vlan_rx_register = bond_vlan_rx_register, - .ndo_vlan_rx_add_vid = bond_vlan_rx_add_vid, + .ndo_vlan_rx_add_vid = bond_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_netpoll_setup = bond_netpoll_setup, @@ -4456,30 +4375,6 @@ static void bond_setup(struct net_device *bond_dev) bond_dev->features |= bond_dev->hw_features; } -static void bond_work_cancel_all(struct bonding *bond) -{ - write_lock_bh(&bond->lock); - bond->kill_timers = 1; - write_unlock_bh(&bond->lock); - - if (bond->params.miimon && delayed_work_pending(&bond->mii_work)) - cancel_delayed_work(&bond->mii_work); - - if (bond->params.arp_interval && delayed_work_pending(&bond->arp_work)) - cancel_delayed_work(&bond->arp_work); - - if (bond->params.mode == BOND_MODE_ALB && - delayed_work_pending(&bond->alb_work)) - cancel_delayed_work(&bond->alb_work); - - if (bond->params.mode == BOND_MODE_8023AD && - delayed_work_pending(&bond->ad_work)) - cancel_delayed_work(&bond->ad_work); - - if (delayed_work_pending(&bond->mcast_work)) - cancel_delayed_work(&bond->mcast_work); -} - /* * Destroy a bonding device. * Must be under rtnl_lock when this function is called. @@ -4498,8 +4393,6 @@ static void bond_uninit(struct net_device *bond_dev) bond_work_cancel_all(bond); - bond_remove_proc_entry(bond); - bond_debug_unregister(bond); __hw_addr_flush(&bond->mc_list); @@ -4788,7 +4681,7 @@ static int bond_check_params(struct bond_params *params) /* miimon and arp_interval not set, we need one so things * work as expected, see bonding.txt for details */ - pr_warning("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n"); + pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details.\n"); } if (primary && !USES_PRIMARY(bond_mode)) { @@ -4845,6 +4738,7 @@ static int bond_check_params(struct bond_params *params) params->tx_queues = tx_queues; params->all_slaves_active = all_slaves_active; params->resend_igmp = resend_igmp; + params->min_links = min_links; if (primary) { strncpy(params->primary, primary, IFNAMSIZ); @@ -4900,7 +4794,6 @@ static int bond_init(struct net_device *bond_dev) bond_set_lockdep_class(bond_dev); - bond_create_proc_entry(bond); list_add_tail(&bond->bond_list, &bn->dev_list); bond_prepare_sysfs_group(bond); @@ -4922,11 +4815,20 @@ static int bond_validate(struct nlattr *tb[], struct nlattr *data[]) return 0; } +static int bond_get_tx_queues(struct net *net, struct nlattr *tb[], + unsigned int *num_queues, + unsigned int *real_num_queues) +{ + *num_queues = tx_queues; + return 0; +} + static struct rtnl_link_ops bond_link_ops __read_mostly = { .kind = "bond", .priv_size = sizeof(struct bonding), .setup = bond_setup, .validate = bond_validate, + .get_tx_queues = bond_get_tx_queues, }; /* Create a new bond based on the specified name and bonding parameters. @@ -4971,6 +4873,7 @@ static int __net_init bond_net_init(struct net *net) INIT_LIST_HEAD(&bn->dev_list); bond_create_proc_dir(bn); + bond_create_sysfs(bn); return 0; } @@ -4978,8 +4881,18 @@ static int __net_init bond_net_init(struct net *net) static void __net_exit bond_net_exit(struct net *net) { struct bond_net *bn = net_generic(net, bond_net_id); + struct bonding *bond, *tmp_bond; + LIST_HEAD(list); + bond_destroy_sysfs(bn); bond_destroy_proc_dir(bn); + + /* Kill off any bonds created after unregistering bond rtnl ops */ + rtnl_lock(); + list_for_each_entry_safe(bond, tmp_bond, &bn->dev_list, bond_list) + unregister_netdevice_queue(bond->dev, &list); + unregister_netdevice_many(&list); + rtnl_unlock(); } static struct pernet_operations bond_net_ops = { @@ -5016,15 +4929,12 @@ static int __init bonding_init(void) goto err; } - res = bond_create_sysfs(); - if (res) - goto err; - register_netdevice_notifier(&bond_netdev_notifier); register_inetaddr_notifier(&bond_inetaddr_notifier); out: return res; err: + bond_destroy_debugfs(); rtnl_link_unregister(&bond_link_ops); err_link: unregister_pernet_subsys(&bond_net_ops); @@ -5037,7 +4947,6 @@ static void __exit bonding_exit(void) unregister_netdevice_notifier(&bond_netdev_notifier); unregister_inetaddr_notifier(&bond_inetaddr_notifier); - bond_destroy_sysfs(); bond_destroy_debugfs(); rtnl_link_unregister(&bond_link_ops); |