diff options
Diffstat (limited to 'drivers/net/team/team.c')
-rw-r--r-- | drivers/net/team/team.c | 203 |
1 files changed, 185 insertions, 18 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index bff7e0b0b4e7..50e43e64d51d 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -622,6 +622,86 @@ static int team_change_mode(struct team *team, const char *kind) | |||
622 | } | 622 | } |
623 | 623 | ||
624 | 624 | ||
625 | /********************* | ||
626 | * Peers notification | ||
627 | *********************/ | ||
628 | |||
629 | static void team_notify_peers_work(struct work_struct *work) | ||
630 | { | ||
631 | struct team *team; | ||
632 | |||
633 | team = container_of(work, struct team, notify_peers.dw.work); | ||
634 | |||
635 | if (!rtnl_trylock()) { | ||
636 | schedule_delayed_work(&team->notify_peers.dw, 0); | ||
637 | return; | ||
638 | } | ||
639 | call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev); | ||
640 | rtnl_unlock(); | ||
641 | if (!atomic_dec_and_test(&team->notify_peers.count_pending)) | ||
642 | schedule_delayed_work(&team->notify_peers.dw, | ||
643 | msecs_to_jiffies(team->notify_peers.interval)); | ||
644 | } | ||
645 | |||
646 | static void team_notify_peers(struct team *team) | ||
647 | { | ||
648 | if (!team->notify_peers.count || !netif_running(team->dev)) | ||
649 | return; | ||
650 | atomic_set(&team->notify_peers.count_pending, team->notify_peers.count); | ||
651 | schedule_delayed_work(&team->notify_peers.dw, 0); | ||
652 | } | ||
653 | |||
654 | static void team_notify_peers_init(struct team *team) | ||
655 | { | ||
656 | INIT_DELAYED_WORK(&team->notify_peers.dw, team_notify_peers_work); | ||
657 | } | ||
658 | |||
659 | static void team_notify_peers_fini(struct team *team) | ||
660 | { | ||
661 | cancel_delayed_work_sync(&team->notify_peers.dw); | ||
662 | } | ||
663 | |||
664 | |||
665 | /******************************* | ||
666 | * Send multicast group rejoins | ||
667 | *******************************/ | ||
668 | |||
669 | static void team_mcast_rejoin_work(struct work_struct *work) | ||
670 | { | ||
671 | struct team *team; | ||
672 | |||
673 | team = container_of(work, struct team, mcast_rejoin.dw.work); | ||
674 | |||
675 | if (!rtnl_trylock()) { | ||
676 | schedule_delayed_work(&team->mcast_rejoin.dw, 0); | ||
677 | return; | ||
678 | } | ||
679 | call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev); | ||
680 | rtnl_unlock(); | ||
681 | if (!atomic_dec_and_test(&team->mcast_rejoin.count_pending)) | ||
682 | schedule_delayed_work(&team->mcast_rejoin.dw, | ||
683 | msecs_to_jiffies(team->mcast_rejoin.interval)); | ||
684 | } | ||
685 | |||
686 | static void team_mcast_rejoin(struct team *team) | ||
687 | { | ||
688 | if (!team->mcast_rejoin.count || !netif_running(team->dev)) | ||
689 | return; | ||
690 | atomic_set(&team->mcast_rejoin.count_pending, team->mcast_rejoin.count); | ||
691 | schedule_delayed_work(&team->mcast_rejoin.dw, 0); | ||
692 | } | ||
693 | |||
694 | static void team_mcast_rejoin_init(struct team *team) | ||
695 | { | ||
696 | INIT_DELAYED_WORK(&team->mcast_rejoin.dw, team_mcast_rejoin_work); | ||
697 | } | ||
698 | |||
699 | static void team_mcast_rejoin_fini(struct team *team) | ||
700 | { | ||
701 | cancel_delayed_work_sync(&team->mcast_rejoin.dw); | ||
702 | } | ||
703 | |||
704 | |||
625 | /************************ | 705 | /************************ |
626 | * Rx path frame handler | 706 | * Rx path frame handler |
627 | ************************/ | 707 | ************************/ |
@@ -846,6 +926,8 @@ static void team_port_enable(struct team *team, | |||
846 | team_queue_override_port_add(team, port); | 926 | team_queue_override_port_add(team, port); |
847 | if (team->ops.port_enabled) | 927 | if (team->ops.port_enabled) |
848 | team->ops.port_enabled(team, port); | 928 | team->ops.port_enabled(team, port); |
929 | team_notify_peers(team); | ||
930 | team_mcast_rejoin(team); | ||
849 | } | 931 | } |
850 | 932 | ||
851 | static void __reconstruct_port_hlist(struct team *team, int rm_index) | 933 | static void __reconstruct_port_hlist(struct team *team, int rm_index) |
@@ -875,6 +957,8 @@ static void team_port_disable(struct team *team, | |||
875 | team->en_port_count--; | 957 | team->en_port_count--; |
876 | team_queue_override_port_del(team, port); | 958 | team_queue_override_port_del(team, port); |
877 | team_adjust_ops(team); | 959 | team_adjust_ops(team); |
960 | team_notify_peers(team); | ||
961 | team_mcast_rejoin(team); | ||
878 | } | 962 | } |
879 | 963 | ||
880 | #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ | 964 | #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ |
@@ -953,6 +1037,9 @@ static int team_port_enable_netpoll(struct team *team, struct team_port *port, | |||
953 | struct netpoll *np; | 1037 | struct netpoll *np; |
954 | int err; | 1038 | int err; |
955 | 1039 | ||
1040 | if (!team->dev->npinfo) | ||
1041 | return 0; | ||
1042 | |||
956 | np = kzalloc(sizeof(*np), gfp); | 1043 | np = kzalloc(sizeof(*np), gfp); |
957 | if (!np) | 1044 | if (!np) |
958 | return -ENOMEM; | 1045 | return -ENOMEM; |
@@ -979,12 +1066,6 @@ static void team_port_disable_netpoll(struct team_port *port) | |||
979 | __netpoll_cleanup(np); | 1066 | __netpoll_cleanup(np); |
980 | kfree(np); | 1067 | kfree(np); |
981 | } | 1068 | } |
982 | |||
983 | static struct netpoll_info *team_netpoll_info(struct team *team) | ||
984 | { | ||
985 | return team->dev->npinfo; | ||
986 | } | ||
987 | |||
988 | #else | 1069 | #else |
989 | static int team_port_enable_netpoll(struct team *team, struct team_port *port, | 1070 | static int team_port_enable_netpoll(struct team *team, struct team_port *port, |
990 | gfp_t gfp) | 1071 | gfp_t gfp) |
@@ -994,10 +1075,6 @@ static int team_port_enable_netpoll(struct team *team, struct team_port *port, | |||
994 | static void team_port_disable_netpoll(struct team_port *port) | 1075 | static void team_port_disable_netpoll(struct team_port *port) |
995 | { | 1076 | { |
996 | } | 1077 | } |
997 | static struct netpoll_info *team_netpoll_info(struct team *team) | ||
998 | { | ||
999 | return NULL; | ||
1000 | } | ||
1001 | #endif | 1078 | #endif |
1002 | 1079 | ||
1003 | static void __team_port_change_port_added(struct team_port *port, bool linkup); | 1080 | static void __team_port_change_port_added(struct team_port *port, bool linkup); |
@@ -1079,13 +1156,11 @@ static int team_port_add(struct team *team, struct net_device *port_dev) | |||
1079 | goto err_vids_add; | 1156 | goto err_vids_add; |
1080 | } | 1157 | } |
1081 | 1158 | ||
1082 | if (team_netpoll_info(team)) { | 1159 | err = team_port_enable_netpoll(team, port, GFP_KERNEL); |
1083 | err = team_port_enable_netpoll(team, port, GFP_KERNEL); | 1160 | if (err) { |
1084 | if (err) { | 1161 | netdev_err(dev, "Failed to enable netpoll on device %s\n", |
1085 | netdev_err(dev, "Failed to enable netpoll on device %s\n", | 1162 | portname); |
1086 | portname); | 1163 | goto err_enable_netpoll; |
1087 | goto err_enable_netpoll; | ||
1088 | } | ||
1089 | } | 1164 | } |
1090 | 1165 | ||
1091 | err = netdev_master_upper_dev_link(port_dev, dev); | 1166 | err = netdev_master_upper_dev_link(port_dev, dev); |
@@ -1205,6 +1280,62 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) | |||
1205 | return team_change_mode(team, ctx->data.str_val); | 1280 | return team_change_mode(team, ctx->data.str_val); |
1206 | } | 1281 | } |
1207 | 1282 | ||
1283 | static int team_notify_peers_count_get(struct team *team, | ||
1284 | struct team_gsetter_ctx *ctx) | ||
1285 | { | ||
1286 | ctx->data.u32_val = team->notify_peers.count; | ||
1287 | return 0; | ||
1288 | } | ||
1289 | |||
1290 | static int team_notify_peers_count_set(struct team *team, | ||
1291 | struct team_gsetter_ctx *ctx) | ||
1292 | { | ||
1293 | team->notify_peers.count = ctx->data.u32_val; | ||
1294 | return 0; | ||
1295 | } | ||
1296 | |||
1297 | static int team_notify_peers_interval_get(struct team *team, | ||
1298 | struct team_gsetter_ctx *ctx) | ||
1299 | { | ||
1300 | ctx->data.u32_val = team->notify_peers.interval; | ||
1301 | return 0; | ||
1302 | } | ||
1303 | |||
1304 | static int team_notify_peers_interval_set(struct team *team, | ||
1305 | struct team_gsetter_ctx *ctx) | ||
1306 | { | ||
1307 | team->notify_peers.interval = ctx->data.u32_val; | ||
1308 | return 0; | ||
1309 | } | ||
1310 | |||
1311 | static int team_mcast_rejoin_count_get(struct team *team, | ||
1312 | struct team_gsetter_ctx *ctx) | ||
1313 | { | ||
1314 | ctx->data.u32_val = team->mcast_rejoin.count; | ||
1315 | return 0; | ||
1316 | } | ||
1317 | |||
1318 | static int team_mcast_rejoin_count_set(struct team *team, | ||
1319 | struct team_gsetter_ctx *ctx) | ||
1320 | { | ||
1321 | team->mcast_rejoin.count = ctx->data.u32_val; | ||
1322 | return 0; | ||
1323 | } | ||
1324 | |||
1325 | static int team_mcast_rejoin_interval_get(struct team *team, | ||
1326 | struct team_gsetter_ctx *ctx) | ||
1327 | { | ||
1328 | ctx->data.u32_val = team->mcast_rejoin.interval; | ||
1329 | return 0; | ||
1330 | } | ||
1331 | |||
1332 | static int team_mcast_rejoin_interval_set(struct team *team, | ||
1333 | struct team_gsetter_ctx *ctx) | ||
1334 | { | ||
1335 | team->mcast_rejoin.interval = ctx->data.u32_val; | ||
1336 | return 0; | ||
1337 | } | ||
1338 | |||
1208 | static int team_port_en_option_get(struct team *team, | 1339 | static int team_port_en_option_get(struct team *team, |
1209 | struct team_gsetter_ctx *ctx) | 1340 | struct team_gsetter_ctx *ctx) |
1210 | { | 1341 | { |
@@ -1317,6 +1448,30 @@ static const struct team_option team_options[] = { | |||
1317 | .setter = team_mode_option_set, | 1448 | .setter = team_mode_option_set, |
1318 | }, | 1449 | }, |
1319 | { | 1450 | { |
1451 | .name = "notify_peers_count", | ||
1452 | .type = TEAM_OPTION_TYPE_U32, | ||
1453 | .getter = team_notify_peers_count_get, | ||
1454 | .setter = team_notify_peers_count_set, | ||
1455 | }, | ||
1456 | { | ||
1457 | .name = "notify_peers_interval", | ||
1458 | .type = TEAM_OPTION_TYPE_U32, | ||
1459 | .getter = team_notify_peers_interval_get, | ||
1460 | .setter = team_notify_peers_interval_set, | ||
1461 | }, | ||
1462 | { | ||
1463 | .name = "mcast_rejoin_count", | ||
1464 | .type = TEAM_OPTION_TYPE_U32, | ||
1465 | .getter = team_mcast_rejoin_count_get, | ||
1466 | .setter = team_mcast_rejoin_count_set, | ||
1467 | }, | ||
1468 | { | ||
1469 | .name = "mcast_rejoin_interval", | ||
1470 | .type = TEAM_OPTION_TYPE_U32, | ||
1471 | .getter = team_mcast_rejoin_interval_get, | ||
1472 | .setter = team_mcast_rejoin_interval_set, | ||
1473 | }, | ||
1474 | { | ||
1320 | .name = "enabled", | 1475 | .name = "enabled", |
1321 | .type = TEAM_OPTION_TYPE_BOOL, | 1476 | .type = TEAM_OPTION_TYPE_BOOL, |
1322 | .per_port = true, | 1477 | .per_port = true, |
@@ -1396,6 +1551,10 @@ static int team_init(struct net_device *dev) | |||
1396 | 1551 | ||
1397 | INIT_LIST_HEAD(&team->option_list); | 1552 | INIT_LIST_HEAD(&team->option_list); |
1398 | INIT_LIST_HEAD(&team->option_inst_list); | 1553 | INIT_LIST_HEAD(&team->option_inst_list); |
1554 | |||
1555 | team_notify_peers_init(team); | ||
1556 | team_mcast_rejoin_init(team); | ||
1557 | |||
1399 | err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); | 1558 | err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); |
1400 | if (err) | 1559 | if (err) |
1401 | goto err_options_register; | 1560 | goto err_options_register; |
@@ -1406,6 +1565,8 @@ static int team_init(struct net_device *dev) | |||
1406 | return 0; | 1565 | return 0; |
1407 | 1566 | ||
1408 | err_options_register: | 1567 | err_options_register: |
1568 | team_mcast_rejoin_fini(team); | ||
1569 | team_notify_peers_fini(team); | ||
1409 | team_queue_override_fini(team); | 1570 | team_queue_override_fini(team); |
1410 | err_team_queue_override_init: | 1571 | err_team_queue_override_init: |
1411 | free_percpu(team->pcpu_stats); | 1572 | free_percpu(team->pcpu_stats); |
@@ -1425,6 +1586,8 @@ static void team_uninit(struct net_device *dev) | |||
1425 | 1586 | ||
1426 | __team_change_mode(team, NULL); /* cleanup */ | 1587 | __team_change_mode(team, NULL); /* cleanup */ |
1427 | __team_options_unregister(team, team_options, ARRAY_SIZE(team_options)); | 1588 | __team_options_unregister(team, team_options, ARRAY_SIZE(team_options)); |
1589 | team_mcast_rejoin_fini(team); | ||
1590 | team_notify_peers_fini(team); | ||
1428 | team_queue_override_fini(team); | 1591 | team_queue_override_fini(team); |
1429 | mutex_unlock(&team->lock); | 1592 | mutex_unlock(&team->lock); |
1430 | } | 1593 | } |
@@ -1811,7 +1974,7 @@ static void team_setup_by_port(struct net_device *dev, | |||
1811 | dev->addr_len = port_dev->addr_len; | 1974 | dev->addr_len = port_dev->addr_len; |
1812 | dev->mtu = port_dev->mtu; | 1975 | dev->mtu = port_dev->mtu; |
1813 | memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len); | 1976 | memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len); |
1814 | memcpy(dev->dev_addr, port_dev->dev_addr, port_dev->addr_len); | 1977 | eth_hw_addr_inherit(dev, port_dev); |
1815 | } | 1978 | } |
1816 | 1979 | ||
1817 | static int team_dev_type_check_change(struct net_device *dev, | 1980 | static int team_dev_type_check_change(struct net_device *dev, |
@@ -2698,6 +2861,10 @@ static int team_device_event(struct notifier_block *unused, | |||
2698 | case NETDEV_PRE_TYPE_CHANGE: | 2861 | case NETDEV_PRE_TYPE_CHANGE: |
2699 | /* Forbid to change type of underlaying device */ | 2862 | /* Forbid to change type of underlaying device */ |
2700 | return NOTIFY_BAD; | 2863 | return NOTIFY_BAD; |
2864 | case NETDEV_RESEND_IGMP: | ||
2865 | /* Propagate to master device */ | ||
2866 | call_netdevice_notifiers(event, port->team->dev); | ||
2867 | break; | ||
2701 | } | 2868 | } |
2702 | return NOTIFY_DONE; | 2869 | return NOTIFY_DONE; |
2703 | } | 2870 | } |