diff options
author | Jiri Pirko <jiri@resnulli.us> | 2012-08-17 00:00:48 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-08-20 05:40:48 -0400 |
commit | 1d76efe1577b4323609b1bcbfafa8b731eda071a (patch) | |
tree | ddf6fddeee02ad33f304dd5479f57208245a3f88 /drivers/net/team/team.c | |
parent | 4c6de2fedc321e17487b74547ad9f73f248faaac (diff) |
team: add support for non-ethernet devices
This is resolved by two things:
1) allow dev_addr of different length than ETH_ALEN
2) during port add, check for dev->type and change it if necessary
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/team/team.c')
-rw-r--r-- | drivers/net/team/team.c | 89 |
1 files changed, 67 insertions, 22 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ba10c469b02b..bc36d13c2675 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -54,29 +54,29 @@ static struct team_port *team_port_get_rtnl(const struct net_device *dev) | |||
54 | } | 54 | } |
55 | 55 | ||
56 | /* | 56 | /* |
57 | * Since the ability to change mac address for open port device is tested in | 57 | * Since the ability to change device address for open port device is tested in |
58 | * team_port_add, this function can be called without control of return value | 58 | * team_port_add, this function can be called without control of return value |
59 | */ | 59 | */ |
60 | static int __set_port_mac(struct net_device *port_dev, | 60 | static int __set_port_dev_addr(struct net_device *port_dev, |
61 | const unsigned char *dev_addr) | 61 | const unsigned char *dev_addr) |
62 | { | 62 | { |
63 | struct sockaddr addr; | 63 | struct sockaddr addr; |
64 | 64 | ||
65 | memcpy(addr.sa_data, dev_addr, ETH_ALEN); | 65 | memcpy(addr.sa_data, dev_addr, port_dev->addr_len); |
66 | addr.sa_family = ARPHRD_ETHER; | 66 | addr.sa_family = port_dev->type; |
67 | return dev_set_mac_address(port_dev, &addr); | 67 | return dev_set_mac_address(port_dev, &addr); |
68 | } | 68 | } |
69 | 69 | ||
70 | static int team_port_set_orig_mac(struct team_port *port) | 70 | static int team_port_set_orig_dev_addr(struct team_port *port) |
71 | { | 71 | { |
72 | return __set_port_mac(port->dev, port->orig.dev_addr); | 72 | return __set_port_dev_addr(port->dev, port->orig.dev_addr); |
73 | } | 73 | } |
74 | 74 | ||
75 | int team_port_set_team_mac(struct team_port *port) | 75 | int team_port_set_team_dev_addr(struct team_port *port) |
76 | { | 76 | { |
77 | return __set_port_mac(port->dev, port->team->dev->dev_addr); | 77 | return __set_port_dev_addr(port->dev, port->team->dev->dev_addr); |
78 | } | 78 | } |
79 | EXPORT_SYMBOL(team_port_set_team_mac); | 79 | EXPORT_SYMBOL(team_port_set_team_dev_addr); |
80 | 80 | ||
81 | static void team_refresh_port_linkup(struct team_port *port) | 81 | static void team_refresh_port_linkup(struct team_port *port) |
82 | { | 82 | { |
@@ -965,6 +965,8 @@ static struct netpoll_info *team_netpoll_info(struct team *team) | |||
965 | #endif | 965 | #endif |
966 | 966 | ||
967 | static void __team_port_change_check(struct team_port *port, bool linkup); | 967 | static void __team_port_change_check(struct team_port *port, bool linkup); |
968 | static int team_dev_type_check_change(struct net_device *dev, | ||
969 | struct net_device *port_dev); | ||
968 | 970 | ||
969 | static int team_port_add(struct team *team, struct net_device *port_dev) | 971 | static int team_port_add(struct team *team, struct net_device *port_dev) |
970 | { | 972 | { |
@@ -973,9 +975,8 @@ static int team_port_add(struct team *team, struct net_device *port_dev) | |||
973 | char *portname = port_dev->name; | 975 | char *portname = port_dev->name; |
974 | int err; | 976 | int err; |
975 | 977 | ||
976 | if (port_dev->flags & IFF_LOOPBACK || | 978 | if (port_dev->flags & IFF_LOOPBACK) { |
977 | port_dev->type != ARPHRD_ETHER) { | 979 | netdev_err(dev, "Device %s is loopback device. Loopback devices can't be added as a team port\n", |
978 | netdev_err(dev, "Device %s is of an unsupported type\n", | ||
979 | portname); | 980 | portname); |
980 | return -EINVAL; | 981 | return -EINVAL; |
981 | } | 982 | } |
@@ -986,6 +987,10 @@ static int team_port_add(struct team *team, struct net_device *port_dev) | |||
986 | return -EBUSY; | 987 | return -EBUSY; |
987 | } | 988 | } |
988 | 989 | ||
990 | err = team_dev_type_check_change(dev, port_dev); | ||
991 | if (err) | ||
992 | return err; | ||
993 | |||
989 | if (port_dev->flags & IFF_UP) { | 994 | if (port_dev->flags & IFF_UP) { |
990 | netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n", | 995 | netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n", |
991 | portname); | 996 | portname); |
@@ -1008,7 +1013,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) | |||
1008 | goto err_set_mtu; | 1013 | goto err_set_mtu; |
1009 | } | 1014 | } |
1010 | 1015 | ||
1011 | memcpy(port->orig.dev_addr, port_dev->dev_addr, ETH_ALEN); | 1016 | memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len); |
1012 | 1017 | ||
1013 | err = team_port_enter(team, port); | 1018 | err = team_port_enter(team, port); |
1014 | if (err) { | 1019 | if (err) { |
@@ -1089,7 +1094,7 @@ err_vids_add: | |||
1089 | 1094 | ||
1090 | err_dev_open: | 1095 | err_dev_open: |
1091 | team_port_leave(team, port); | 1096 | team_port_leave(team, port); |
1092 | team_port_set_orig_mac(port); | 1097 | team_port_set_orig_dev_addr(port); |
1093 | 1098 | ||
1094 | err_port_enter: | 1099 | err_port_enter: |
1095 | dev_set_mtu(port_dev, port->orig.mtu); | 1100 | dev_set_mtu(port_dev, port->orig.mtu); |
@@ -1126,7 +1131,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) | |||
1126 | vlan_vids_del_by_dev(port_dev, dev); | 1131 | vlan_vids_del_by_dev(port_dev, dev); |
1127 | dev_close(port_dev); | 1132 | dev_close(port_dev); |
1128 | team_port_leave(team, port); | 1133 | team_port_leave(team, port); |
1129 | team_port_set_orig_mac(port); | 1134 | team_port_set_orig_dev_addr(port); |
1130 | dev_set_mtu(port_dev, port->orig.mtu); | 1135 | dev_set_mtu(port_dev, port->orig.mtu); |
1131 | synchronize_rcu(); | 1136 | synchronize_rcu(); |
1132 | kfree(port); | 1137 | kfree(port); |
@@ -1477,17 +1482,18 @@ static void team_set_rx_mode(struct net_device *dev) | |||
1477 | 1482 | ||
1478 | static int team_set_mac_address(struct net_device *dev, void *p) | 1483 | static int team_set_mac_address(struct net_device *dev, void *p) |
1479 | { | 1484 | { |
1485 | struct sockaddr *addr = p; | ||
1480 | struct team *team = netdev_priv(dev); | 1486 | struct team *team = netdev_priv(dev); |
1481 | struct team_port *port; | 1487 | struct team_port *port; |
1482 | int err; | ||
1483 | 1488 | ||
1484 | err = eth_mac_addr(dev, p); | 1489 | if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data)) |
1485 | if (err) | 1490 | return -EADDRNOTAVAIL; |
1486 | return err; | 1491 | memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); |
1492 | dev->addr_assign_type &= ~NET_ADDR_RANDOM; | ||
1487 | rcu_read_lock(); | 1493 | rcu_read_lock(); |
1488 | list_for_each_entry_rcu(port, &team->port_list, list) | 1494 | list_for_each_entry_rcu(port, &team->port_list, list) |
1489 | if (team->ops.port_change_mac) | 1495 | if (team->ops.port_change_dev_addr) |
1490 | team->ops.port_change_mac(team, port); | 1496 | team->ops.port_change_dev_addr(team, port); |
1491 | rcu_read_unlock(); | 1497 | rcu_read_unlock(); |
1492 | return 0; | 1498 | return 0; |
1493 | } | 1499 | } |
@@ -1718,6 +1724,45 @@ static const struct net_device_ops team_netdev_ops = { | |||
1718 | * rt netlink interface | 1724 | * rt netlink interface |
1719 | ***********************/ | 1725 | ***********************/ |
1720 | 1726 | ||
1727 | static void team_setup_by_port(struct net_device *dev, | ||
1728 | struct net_device *port_dev) | ||
1729 | { | ||
1730 | dev->header_ops = port_dev->header_ops; | ||
1731 | dev->type = port_dev->type; | ||
1732 | dev->hard_header_len = port_dev->hard_header_len; | ||
1733 | dev->addr_len = port_dev->addr_len; | ||
1734 | dev->mtu = port_dev->mtu; | ||
1735 | memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len); | ||
1736 | memcpy(dev->dev_addr, port_dev->dev_addr, port_dev->addr_len); | ||
1737 | dev->addr_assign_type &= ~NET_ADDR_RANDOM; | ||
1738 | } | ||
1739 | |||
1740 | static int team_dev_type_check_change(struct net_device *dev, | ||
1741 | struct net_device *port_dev) | ||
1742 | { | ||
1743 | struct team *team = netdev_priv(dev); | ||
1744 | char *portname = port_dev->name; | ||
1745 | int err; | ||
1746 | |||
1747 | if (dev->type == port_dev->type) | ||
1748 | return 0; | ||
1749 | if (!list_empty(&team->port_list)) { | ||
1750 | netdev_err(dev, "Device %s is of different type\n", portname); | ||
1751 | return -EBUSY; | ||
1752 | } | ||
1753 | err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev); | ||
1754 | err = notifier_to_errno(err); | ||
1755 | if (err) { | ||
1756 | netdev_err(dev, "Refused to change device type\n"); | ||
1757 | return err; | ||
1758 | } | ||
1759 | dev_uc_flush(dev); | ||
1760 | dev_mc_flush(dev); | ||
1761 | team_setup_by_port(dev, port_dev); | ||
1762 | call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev); | ||
1763 | return 0; | ||
1764 | } | ||
1765 | |||
1721 | static void team_setup(struct net_device *dev) | 1766 | static void team_setup(struct net_device *dev) |
1722 | { | 1767 | { |
1723 | ether_setup(dev); | 1768 | ether_setup(dev); |