diff options
Diffstat (limited to 'drivers/net/bonding')
-rw-r--r-- | drivers/net/bonding/bond_ipv6.c | 225 | ||||
-rw-r--r-- | drivers/net/bonding/bond_main.c | 83 |
2 files changed, 44 insertions, 264 deletions
diff --git a/drivers/net/bonding/bond_ipv6.c b/drivers/net/bonding/bond_ipv6.c deleted file mode 100644 index 027a0ee7d85..00000000000 --- a/drivers/net/bonding/bond_ipv6.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright(c) 2008 Hewlett-Packard Development Company, L.P. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/types.h> -#include <linux/if_vlan.h> -#include <net/ipv6.h> -#include <net/ndisc.h> -#include <net/addrconf.h> -#include <net/netns/generic.h> -#include "bonding.h" - -/* - * Assign bond->master_ipv6 to the next IPv6 address in the list, or - * zero it out if there are none. - */ -static void bond_glean_dev_ipv6(struct net_device *dev, struct in6_addr *addr) -{ - struct inet6_dev *idev; - - if (!dev) - return; - - idev = in6_dev_get(dev); - if (!idev) - return; - - read_lock_bh(&idev->lock); - if (!list_empty(&idev->addr_list)) { - struct inet6_ifaddr *ifa - = list_first_entry(&idev->addr_list, - struct inet6_ifaddr, if_list); - ipv6_addr_copy(addr, &ifa->addr); - } else - ipv6_addr_set(addr, 0, 0, 0, 0); - - read_unlock_bh(&idev->lock); - - in6_dev_put(idev); -} - -static void bond_na_send(struct net_device *slave_dev, - struct in6_addr *daddr, - int router, - unsigned short vlan_id) -{ - struct in6_addr mcaddr; - struct icmp6hdr icmp6h = { - .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, - }; - struct sk_buff *skb; - - icmp6h.icmp6_router = router; - icmp6h.icmp6_solicited = 0; - icmp6h.icmp6_override = 1; - - addrconf_addr_solict_mult(daddr, &mcaddr); - - pr_debug("ipv6 na on slave %s: dest %pI6, src %pI6\n", - slave_dev->name, &mcaddr, daddr); - - skb = ndisc_build_skb(slave_dev, &mcaddr, daddr, &icmp6h, daddr, - ND_OPT_TARGET_LL_ADDR); - - if (!skb) { - pr_err("NA packet allocation failed\n"); - return; - } - - if (vlan_id) { - /* The Ethernet header is not present yet, so it is - * too early to insert a VLAN tag. Force use of an - * out-of-line tag here and let dev_hard_start_xmit() - * insert it if the slave hardware can't. - */ - skb = __vlan_hwaccel_put_tag(skb, vlan_id); - if (!skb) { - pr_err("failed to insert VLAN tag\n"); - return; - } - } - - ndisc_send_skb(skb, slave_dev, NULL, &mcaddr, daddr, &icmp6h); -} - -/* - * Kick out an unsolicited Neighbor Advertisement for an IPv6 address on - * the bonding master. This will help the switch learn our address - * if in active-backup mode. - * - * Caller must hold curr_slave_lock for read or better - */ -void bond_send_unsolicited_na(struct bonding *bond) -{ - struct slave *slave = bond->curr_active_slave; - struct vlan_entry *vlan; - struct inet6_dev *idev; - int is_router; - - pr_debug("%s: bond %s slave %s\n", bond->dev->name, - __func__, slave ? slave->dev->name : "NULL"); - - if (!slave || !bond->send_unsol_na || - test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state)) - return; - - bond->send_unsol_na--; - - idev = in6_dev_get(bond->dev); - if (!idev) - return; - - is_router = !!idev->cnf.forwarding; - - in6_dev_put(idev); - - if (!ipv6_addr_any(&bond->master_ipv6)) - bond_na_send(slave->dev, &bond->master_ipv6, is_router, 0); - - list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - if (!ipv6_addr_any(&vlan->vlan_ipv6)) { - bond_na_send(slave->dev, &vlan->vlan_ipv6, is_router, - vlan->vlan_id); - } - } -} - -/* - * bond_inet6addr_event: handle inet6addr notifier chain events. - * - * We keep track of device IPv6 addresses primarily to use as source - * addresses in NS probes. - * - * We track one IPv6 for the main device (if it has one). - */ -static int bond_inet6addr_event(struct notifier_block *this, - unsigned long event, - void *ptr) -{ - struct inet6_ifaddr *ifa = ptr; - struct net_device *vlan_dev, *event_dev = ifa->idev->dev; - struct bonding *bond; - struct vlan_entry *vlan; - struct bond_net *bn = net_generic(dev_net(event_dev), bond_net_id); - - list_for_each_entry(bond, &bn->dev_list, bond_list) { - if (bond->dev == event_dev) { - switch (event) { - case NETDEV_UP: - if (ipv6_addr_any(&bond->master_ipv6)) - ipv6_addr_copy(&bond->master_ipv6, - &ifa->addr); - return NOTIFY_OK; - case NETDEV_DOWN: - if (ipv6_addr_equal(&bond->master_ipv6, - &ifa->addr)) - bond_glean_dev_ipv6(bond->dev, - &bond->master_ipv6); - return NOTIFY_OK; - default: - return NOTIFY_DONE; - } - } - - 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 == event_dev) { - switch (event) { - case NETDEV_UP: - if (ipv6_addr_any(&vlan->vlan_ipv6)) - ipv6_addr_copy(&vlan->vlan_ipv6, - &ifa->addr); - return NOTIFY_OK; - case NETDEV_DOWN: - if (ipv6_addr_equal(&vlan->vlan_ipv6, - &ifa->addr)) - bond_glean_dev_ipv6(vlan_dev, - &vlan->vlan_ipv6); - return NOTIFY_OK; - default: - return NOTIFY_DONE; - } - } - } - } - return NOTIFY_DONE; -} - -static struct notifier_block bond_inet6addr_notifier = { - .notifier_call = bond_inet6addr_event, -}; - -void bond_register_ipv6_notifier(void) -{ - register_inet6addr_notifier(&bond_inet6addr_notifier); -} - -void bond_unregister_ipv6_notifier(void) -{ - unregister_inet6addr_notifier(&bond_inet6addr_notifier); -} - diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 7f8756825b8..435984ad8b2 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -428,27 +428,34 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, * @bond_dev: bonding net device that got called * @vid: vlan id being added */ -static void bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid) +static int bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid) { struct bonding *bond = netdev_priv(bond_dev); - struct slave *slave; + struct slave *slave, *stop_at; int i, res; 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_FILTER) && - slave_ops->ndo_vlan_rx_add_vid) { - slave_ops->ndo_vlan_rx_add_vid(slave_dev, vid); - } + res = vlan_vid_add(slave->dev, vid); + if (res) + goto unwind; } res = bond_add_vlan(bond, vid); if (res) { pr_err("%s: Error: Failed to add vlan id %d\n", bond_dev->name, vid); + return res; } + + return 0; + +unwind: + /* unwind from head to the slave that failed */ + stop_at = slave; + bond_for_each_slave_from_to(bond, slave, i, bond->first_slave, stop_at) + vlan_vid_del(slave->dev, vid); + + return res; } /** @@ -456,56 +463,48 @@ static void bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid) * @bond_dev: bonding net device that got called * @vid: vlan id being removed */ -static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid) +static int bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid) { struct bonding *bond = netdev_priv(bond_dev); struct slave *slave; int i, res; - 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_FILTER) && - slave_ops->ndo_vlan_rx_kill_vid) { - slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vid); - } - } + bond_for_each_slave(bond, slave, i) + vlan_vid_del(slave->dev, vid); res = bond_del_vlan(bond, vid); if (res) { pr_err("%s: Error: Failed to remove vlan id %d\n", bond_dev->name, vid); + return res; } + + return 0; } static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *slave_dev) { struct vlan_entry *vlan; - const struct net_device_ops *slave_ops = slave_dev->netdev_ops; - - if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) || - !(slave_ops->ndo_vlan_rx_add_vid)) - return; + int res; - list_for_each_entry(vlan, &bond->vlan_list, vlan_list) - slave_ops->ndo_vlan_rx_add_vid(slave_dev, vlan->vlan_id); + list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { + res = vlan_vid_add(slave_dev, vlan->vlan_id); + if (res) + pr_warning("%s: Failed to add vlan id %d to device %s\n", + bond->dev->name, vlan->vlan_id, + slave_dev->name); + } } static void bond_del_vlans_from_slave(struct bonding *bond, struct net_device *slave_dev) { - const struct net_device_ops *slave_ops = slave_dev->netdev_ops; struct vlan_entry *vlan; - if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) || - !(slave_ops->ndo_vlan_rx_kill_vid)) - return; - list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { if (!vlan->vlan_id) continue; - slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vlan->vlan_id); + vlan_vid_del(slave_dev, vlan->vlan_id); } } @@ -1325,11 +1324,12 @@ static int bond_sethwaddr(struct net_device *bond_dev, return 0; } -static u32 bond_fix_features(struct net_device *dev, u32 features) +static netdev_features_t bond_fix_features(struct net_device *dev, + netdev_features_t features) { struct slave *slave; struct bonding *bond = netdev_priv(dev); - u32 mask; + netdev_features_t mask; int i; read_lock(&bond->lock); @@ -1363,7 +1363,7 @@ static void bond_compute_features(struct bonding *bond) { struct slave *slave; struct net_device *bond_dev = bond->dev; - u32 vlan_features = BOND_VLAN_FEATURES; + netdev_features_t vlan_features = BOND_VLAN_FEATURES; unsigned short max_hard_header_len = ETH_HLEN; int i; @@ -1822,7 +1822,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) "but new slave device does not support netpoll.\n", bond_dev->name); res = -EBUSY; - goto err_close; + goto err_detach; } } #endif @@ -1831,7 +1831,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) res = bond_create_slave_symlinks(bond_dev, slave_dev); if (res) - goto err_close; + goto err_detach; res = netdev_rx_handler_register(slave_dev, bond_handle_frame, new_slave); @@ -1852,6 +1852,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) err_dest_symlinks: bond_destroy_slave_symlinks(bond_dev, slave_dev); +err_detach: + write_lock_bh(&bond->lock); + bond_detach_slave(bond, new_slave); + write_unlock_bh(&bond->lock); + err_close: dev_close(slave_dev); @@ -1897,7 +1902,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) struct bonding *bond = netdev_priv(bond_dev); struct slave *slave, *oldcurrent; struct sockaddr addr; - u32 old_features = bond_dev->features; + netdev_features_t old_features = bond_dev->features; /* slave is not a slave or master is not master of this slave */ if (!(slave_dev->flags & IFF_SLAVE) || @@ -4339,7 +4344,7 @@ static void bond_setup(struct net_device *bond_dev) NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; - bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_NO_CSUM); + bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM); bond_dev->features |= bond_dev->hw_features; } |