diff options
author | Jiri Pirko <jiri@resnulli.us> | 2012-07-17 01:22:36 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-07-17 12:02:36 -0400 |
commit | bd2d0837abc0206ecdd3f6b9fc8c25b55b63c96b (patch) | |
tree | d420a4e51965ae8b680562535d7ec9aace815f8e /drivers/net/team/team.c | |
parent | 30fdd8a082a00126a6feec994e43e8dc12f5bccb (diff) |
team: add netpoll support
It's done in very similar way this is done in bonding and bridge.
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 | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 3620c63f9345..1a13470dee07 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/ctype.h> | 18 | #include <linux/ctype.h> |
19 | #include <linux/notifier.h> | 19 | #include <linux/notifier.h> |
20 | #include <linux/netdevice.h> | 20 | #include <linux/netdevice.h> |
21 | #include <linux/netpoll.h> | ||
21 | #include <linux/if_vlan.h> | 22 | #include <linux/if_vlan.h> |
22 | #include <linux/if_arp.h> | 23 | #include <linux/if_arp.h> |
23 | #include <linux/socket.h> | 24 | #include <linux/socket.h> |
@@ -787,6 +788,58 @@ static void team_port_leave(struct team *team, struct team_port *port) | |||
787 | dev_put(team->dev); | 788 | dev_put(team->dev); |
788 | } | 789 | } |
789 | 790 | ||
791 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
792 | static int team_port_enable_netpoll(struct team *team, struct team_port *port) | ||
793 | { | ||
794 | struct netpoll *np; | ||
795 | int err; | ||
796 | |||
797 | np = kzalloc(sizeof(*np), GFP_KERNEL); | ||
798 | if (!np) | ||
799 | return -ENOMEM; | ||
800 | |||
801 | err = __netpoll_setup(np, port->dev); | ||
802 | if (err) { | ||
803 | kfree(np); | ||
804 | return err; | ||
805 | } | ||
806 | port->np = np; | ||
807 | return err; | ||
808 | } | ||
809 | |||
810 | static void team_port_disable_netpoll(struct team_port *port) | ||
811 | { | ||
812 | struct netpoll *np = port->np; | ||
813 | |||
814 | if (!np) | ||
815 | return; | ||
816 | port->np = NULL; | ||
817 | |||
818 | /* Wait for transmitting packets to finish before freeing. */ | ||
819 | synchronize_rcu_bh(); | ||
820 | __netpoll_cleanup(np); | ||
821 | kfree(np); | ||
822 | } | ||
823 | |||
824 | static struct netpoll_info *team_netpoll_info(struct team *team) | ||
825 | { | ||
826 | return team->dev->npinfo; | ||
827 | } | ||
828 | |||
829 | #else | ||
830 | static int team_port_enable_netpoll(struct team *team, struct team_port *port) | ||
831 | { | ||
832 | return 0; | ||
833 | } | ||
834 | static void team_port_disable_netpoll(struct team_port *port) | ||
835 | { | ||
836 | } | ||
837 | static struct netpoll_info *team_netpoll_info(struct team *team) | ||
838 | { | ||
839 | return NULL; | ||
840 | } | ||
841 | #endif | ||
842 | |||
790 | static void __team_port_change_check(struct team_port *port, bool linkup); | 843 | static void __team_port_change_check(struct team_port *port, bool linkup); |
791 | 844 | ||
792 | static int team_port_add(struct team *team, struct net_device *port_dev) | 845 | static int team_port_add(struct team *team, struct net_device *port_dev) |
@@ -853,6 +906,15 @@ static int team_port_add(struct team *team, struct net_device *port_dev) | |||
853 | goto err_vids_add; | 906 | goto err_vids_add; |
854 | } | 907 | } |
855 | 908 | ||
909 | if (team_netpoll_info(team)) { | ||
910 | err = team_port_enable_netpoll(team, port); | ||
911 | if (err) { | ||
912 | netdev_err(dev, "Failed to enable netpoll on device %s\n", | ||
913 | portname); | ||
914 | goto err_enable_netpoll; | ||
915 | } | ||
916 | } | ||
917 | |||
856 | err = netdev_set_master(port_dev, dev); | 918 | err = netdev_set_master(port_dev, dev); |
857 | if (err) { | 919 | if (err) { |
858 | netdev_err(dev, "Device %s failed to set master\n", portname); | 920 | netdev_err(dev, "Device %s failed to set master\n", portname); |
@@ -892,6 +954,9 @@ err_handler_register: | |||
892 | netdev_set_master(port_dev, NULL); | 954 | netdev_set_master(port_dev, NULL); |
893 | 955 | ||
894 | err_set_master: | 956 | err_set_master: |
957 | team_port_disable_netpoll(port); | ||
958 | |||
959 | err_enable_netpoll: | ||
895 | vlan_vids_del_by_dev(port_dev, dev); | 960 | vlan_vids_del_by_dev(port_dev, dev); |
896 | 961 | ||
897 | err_vids_add: | 962 | err_vids_add: |
@@ -932,6 +997,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) | |||
932 | list_del_rcu(&port->list); | 997 | list_del_rcu(&port->list); |
933 | netdev_rx_handler_unregister(port_dev); | 998 | netdev_rx_handler_unregister(port_dev); |
934 | netdev_set_master(port_dev, NULL); | 999 | netdev_set_master(port_dev, NULL); |
1000 | team_port_disable_netpoll(port); | ||
935 | vlan_vids_del_by_dev(port_dev, dev); | 1001 | vlan_vids_del_by_dev(port_dev, dev); |
936 | dev_close(port_dev); | 1002 | dev_close(port_dev); |
937 | team_port_leave(team, port); | 1003 | team_port_leave(team, port); |
@@ -1307,6 +1373,48 @@ static int team_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid) | |||
1307 | return 0; | 1373 | return 0; |
1308 | } | 1374 | } |
1309 | 1375 | ||
1376 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
1377 | static void team_poll_controller(struct net_device *dev) | ||
1378 | { | ||
1379 | } | ||
1380 | |||
1381 | static void __team_netpoll_cleanup(struct team *team) | ||
1382 | { | ||
1383 | struct team_port *port; | ||
1384 | |||
1385 | list_for_each_entry(port, &team->port_list, list) | ||
1386 | team_port_disable_netpoll(port); | ||
1387 | } | ||
1388 | |||
1389 | static void team_netpoll_cleanup(struct net_device *dev) | ||
1390 | { | ||
1391 | struct team *team = netdev_priv(dev); | ||
1392 | |||
1393 | mutex_lock(&team->lock); | ||
1394 | __team_netpoll_cleanup(team); | ||
1395 | mutex_unlock(&team->lock); | ||
1396 | } | ||
1397 | |||
1398 | static int team_netpoll_setup(struct net_device *dev, | ||
1399 | struct netpoll_info *npifo) | ||
1400 | { | ||
1401 | struct team *team = netdev_priv(dev); | ||
1402 | struct team_port *port; | ||
1403 | int err; | ||
1404 | |||
1405 | mutex_lock(&team->lock); | ||
1406 | list_for_each_entry(port, &team->port_list, list) { | ||
1407 | err = team_port_enable_netpoll(team, port); | ||
1408 | if (err) { | ||
1409 | __team_netpoll_cleanup(team); | ||
1410 | break; | ||
1411 | } | ||
1412 | } | ||
1413 | mutex_unlock(&team->lock); | ||
1414 | return err; | ||
1415 | } | ||
1416 | #endif | ||
1417 | |||
1310 | static int team_add_slave(struct net_device *dev, struct net_device *port_dev) | 1418 | static int team_add_slave(struct net_device *dev, struct net_device *port_dev) |
1311 | { | 1419 | { |
1312 | struct team *team = netdev_priv(dev); | 1420 | struct team *team = netdev_priv(dev); |
@@ -1363,6 +1471,11 @@ static const struct net_device_ops team_netdev_ops = { | |||
1363 | .ndo_get_stats64 = team_get_stats64, | 1471 | .ndo_get_stats64 = team_get_stats64, |
1364 | .ndo_vlan_rx_add_vid = team_vlan_rx_add_vid, | 1472 | .ndo_vlan_rx_add_vid = team_vlan_rx_add_vid, |
1365 | .ndo_vlan_rx_kill_vid = team_vlan_rx_kill_vid, | 1473 | .ndo_vlan_rx_kill_vid = team_vlan_rx_kill_vid, |
1474 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
1475 | .ndo_poll_controller = team_poll_controller, | ||
1476 | .ndo_netpoll_setup = team_netpoll_setup, | ||
1477 | .ndo_netpoll_cleanup = team_netpoll_cleanup, | ||
1478 | #endif | ||
1366 | .ndo_add_slave = team_add_slave, | 1479 | .ndo_add_slave = team_add_slave, |
1367 | .ndo_del_slave = team_del_slave, | 1480 | .ndo_del_slave = team_del_slave, |
1368 | .ndo_fix_features = team_fix_features, | 1481 | .ndo_fix_features = team_fix_features, |