diff options
-rw-r--r-- | drivers/net/team/team.c | 113 | ||||
-rw-r--r-- | drivers/net/team/team_mode_activebackup.c | 3 | ||||
-rw-r--r-- | drivers/net/team/team_mode_broadcast.c | 7 | ||||
-rw-r--r-- | drivers/net/team/team_mode_loadbalance.c | 3 | ||||
-rw-r--r-- | drivers/net/team/team_mode_roundrobin.c | 3 | ||||
-rw-r--r-- | include/linux/if_team.h | 33 |
6 files changed, 152 insertions, 10 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, |
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index 253b8a5f3427..6262b4defd93 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c | |||
@@ -43,8 +43,7 @@ static bool ab_transmit(struct team *team, struct sk_buff *skb) | |||
43 | active_port = rcu_dereference_bh(ab_priv(team)->active_port); | 43 | active_port = rcu_dereference_bh(ab_priv(team)->active_port); |
44 | if (unlikely(!active_port)) | 44 | if (unlikely(!active_port)) |
45 | goto drop; | 45 | goto drop; |
46 | skb->dev = active_port->dev; | 46 | if (team_dev_queue_xmit(team, active_port, skb)) |
47 | if (dev_queue_xmit(skb)) | ||
48 | return false; | 47 | return false; |
49 | return true; | 48 | return true; |
50 | 49 | ||
diff --git a/drivers/net/team/team_mode_broadcast.c b/drivers/net/team/team_mode_broadcast.c index 5562345e9cef..c96e4d2967f0 100644 --- a/drivers/net/team/team_mode_broadcast.c +++ b/drivers/net/team/team_mode_broadcast.c | |||
@@ -29,8 +29,8 @@ static bool bc_transmit(struct team *team, struct sk_buff *skb) | |||
29 | if (last) { | 29 | if (last) { |
30 | skb2 = skb_clone(skb, GFP_ATOMIC); | 30 | skb2 = skb_clone(skb, GFP_ATOMIC); |
31 | if (skb2) { | 31 | if (skb2) { |
32 | skb2->dev = last->dev; | 32 | ret = team_dev_queue_xmit(team, last, |
33 | ret = dev_queue_xmit(skb2); | 33 | skb2); |
34 | if (!sum_ret) | 34 | if (!sum_ret) |
35 | sum_ret = ret; | 35 | sum_ret = ret; |
36 | } | 36 | } |
@@ -39,8 +39,7 @@ static bool bc_transmit(struct team *team, struct sk_buff *skb) | |||
39 | } | 39 | } |
40 | } | 40 | } |
41 | if (last) { | 41 | if (last) { |
42 | skb->dev = last->dev; | 42 | ret = team_dev_queue_xmit(team, last, skb); |
43 | ret = dev_queue_xmit(skb); | ||
44 | if (!sum_ret) | 43 | if (!sum_ret) |
45 | sum_ret = ret; | 44 | sum_ret = ret; |
46 | } | 45 | } |
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index 51a4b199c75c..cdc31b5ea15e 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c | |||
@@ -217,8 +217,7 @@ static bool lb_transmit(struct team *team, struct sk_buff *skb) | |||
217 | port = select_tx_port_func(team, lb_priv, skb, hash); | 217 | port = select_tx_port_func(team, lb_priv, skb, hash); |
218 | if (unlikely(!port)) | 218 | if (unlikely(!port)) |
219 | goto drop; | 219 | goto drop; |
220 | skb->dev = port->dev; | 220 | if (team_dev_queue_xmit(team, port, skb)) |
221 | if (dev_queue_xmit(skb)) | ||
222 | return false; | 221 | return false; |
223 | lb_update_tx_stats(tx_bytes, lb_priv, get_lb_port_priv(port), hash); | 222 | lb_update_tx_stats(tx_bytes, lb_priv, get_lb_port_priv(port), hash); |
224 | return true; | 223 | return true; |
diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index 0cf38e9b9d26..ad7ed0ec544c 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c | |||
@@ -55,8 +55,7 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb) | |||
55 | port = __get_first_port_up(team, port); | 55 | port = __get_first_port_up(team, port); |
56 | if (unlikely(!port)) | 56 | if (unlikely(!port)) |
57 | goto drop; | 57 | goto drop; |
58 | skb->dev = port->dev; | 58 | if (team_dev_queue_xmit(team, port, skb)) |
59 | if (dev_queue_xmit(skb)) | ||
60 | return false; | 59 | return false; |
61 | return true; | 60 | return true; |
62 | 61 | ||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h index dfa0c8e0ab84..7fd0cdeb9444 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h | |||
@@ -13,6 +13,8 @@ | |||
13 | 13 | ||
14 | #ifdef __KERNEL__ | 14 | #ifdef __KERNEL__ |
15 | 15 | ||
16 | #include <linux/netpoll.h> | ||
17 | |||
16 | struct team_pcpu_stats { | 18 | struct team_pcpu_stats { |
17 | u64 rx_packets; | 19 | u64 rx_packets; |
18 | u64 rx_bytes; | 20 | u64 rx_bytes; |
@@ -60,6 +62,10 @@ struct team_port { | |||
60 | unsigned int mtu; | 62 | unsigned int mtu; |
61 | } orig; | 63 | } orig; |
62 | 64 | ||
65 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
66 | struct netpoll *np; | ||
67 | #endif | ||
68 | |||
63 | long mode_priv[0]; | 69 | long mode_priv[0]; |
64 | }; | 70 | }; |
65 | 71 | ||
@@ -73,6 +79,33 @@ static inline bool team_port_txable(struct team_port *port) | |||
73 | return port->linkup && team_port_enabled(port); | 79 | return port->linkup && team_port_enabled(port); |
74 | } | 80 | } |
75 | 81 | ||
82 | #ifdef CONFIG_NET_POLL_CONTROLLER | ||
83 | static inline void team_netpoll_send_skb(struct team_port *port, | ||
84 | struct sk_buff *skb) | ||
85 | { | ||
86 | struct netpoll *np = port->np; | ||
87 | |||
88 | if (np) | ||
89 | netpoll_send_skb(np, skb); | ||
90 | } | ||
91 | #else | ||
92 | static inline void team_netpoll_send_skb(struct team_port *port, | ||
93 | struct sk_buff *skb) | ||
94 | { | ||
95 | } | ||
96 | #endif | ||
97 | |||
98 | static inline int team_dev_queue_xmit(struct team *team, struct team_port *port, | ||
99 | struct sk_buff *skb) | ||
100 | { | ||
101 | skb->dev = port->dev; | ||
102 | if (unlikely(netpoll_tx_running(port->dev))) { | ||
103 | team_netpoll_send_skb(port, skb); | ||
104 | return 0; | ||
105 | } | ||
106 | return dev_queue_xmit(skb); | ||
107 | } | ||
108 | |||
76 | struct team_mode_ops { | 109 | struct team_mode_ops { |
77 | int (*init)(struct team *team); | 110 | int (*init)(struct team *team); |
78 | void (*exit)(struct team *team); | 111 | void (*exit)(struct team *team); |