diff options
author | Jiri Pirko <jpirko@redhat.com> | 2012-06-26 02:52:45 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-06-27 00:09:39 -0400 |
commit | 122bb046bcfb8f3d2d94d9f7f6b74da187a740f2 (patch) | |
tree | 2a01688c91a1040edd2f939078da7fe4c4eb58da /drivers/net/team | |
parent | 72fd5a8b75fb9962295a8c1338e13a4b1536714a (diff) |
team: fix team_adjust_ops with regard to enabled ports
team_adjust_ops should check for enabled ports, not all ports.
This may lead to division by zero. This patch fixes this.
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/team')
-rw-r--r-- | drivers/net/team/team.c | 30 |
1 files changed, 19 insertions, 11 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 3a4a74be52d9..6b4cf6eca238 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -508,26 +508,31 @@ static void team_set_no_mode(struct team *team) | |||
508 | team->mode = &__team_no_mode; | 508 | team->mode = &__team_no_mode; |
509 | } | 509 | } |
510 | 510 | ||
511 | static void team_adjust_ops(struct team *team) | 511 | static void __team_adjust_ops(struct team *team, int en_port_count) |
512 | { | 512 | { |
513 | /* | 513 | /* |
514 | * To avoid checks in rx/tx skb paths, ensure here that non-null and | 514 | * To avoid checks in rx/tx skb paths, ensure here that non-null and |
515 | * correct ops are always set. | 515 | * correct ops are always set. |
516 | */ | 516 | */ |
517 | 517 | ||
518 | if (list_empty(&team->port_list) || | 518 | if (!en_port_count || !team_is_mode_set(team) || |
519 | !team_is_mode_set(team) || !team->mode->ops->transmit) | 519 | !team->mode->ops->transmit) |
520 | team->ops.transmit = team_dummy_transmit; | 520 | team->ops.transmit = team_dummy_transmit; |
521 | else | 521 | else |
522 | team->ops.transmit = team->mode->ops->transmit; | 522 | team->ops.transmit = team->mode->ops->transmit; |
523 | 523 | ||
524 | if (list_empty(&team->port_list) || | 524 | if (!en_port_count || !team_is_mode_set(team) || |
525 | !team_is_mode_set(team) || !team->mode->ops->receive) | 525 | !team->mode->ops->receive) |
526 | team->ops.receive = team_dummy_receive; | 526 | team->ops.receive = team_dummy_receive; |
527 | else | 527 | else |
528 | team->ops.receive = team->mode->ops->receive; | 528 | team->ops.receive = team->mode->ops->receive; |
529 | } | 529 | } |
530 | 530 | ||
531 | static void team_adjust_ops(struct team *team) | ||
532 | { | ||
533 | __team_adjust_ops(team, team->en_port_count); | ||
534 | } | ||
535 | |||
531 | /* | 536 | /* |
532 | * We can benefit from the fact that it's ensured no port is present | 537 | * We can benefit from the fact that it's ensured no port is present |
533 | * at the time of mode change. Therefore no packets are in fly so there's no | 538 | * at the time of mode change. Therefore no packets are in fly so there's no |
@@ -687,6 +692,7 @@ static void team_port_enable(struct team *team, | |||
687 | port->index = team->en_port_count++; | 692 | port->index = team->en_port_count++; |
688 | hlist_add_head_rcu(&port->hlist, | 693 | hlist_add_head_rcu(&port->hlist, |
689 | team_port_index_hash(team, port->index)); | 694 | team_port_index_hash(team, port->index)); |
695 | team_adjust_ops(team); | ||
690 | if (team->ops.port_enabled) | 696 | if (team->ops.port_enabled) |
691 | team->ops.port_enabled(team, port); | 697 | team->ops.port_enabled(team, port); |
692 | } | 698 | } |
@@ -708,16 +714,20 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index) | |||
708 | static void team_port_disable(struct team *team, | 714 | static void team_port_disable(struct team *team, |
709 | struct team_port *port) | 715 | struct team_port *port) |
710 | { | 716 | { |
711 | int rm_index = port->index; | ||
712 | |||
713 | if (!team_port_enabled(port)) | 717 | if (!team_port_enabled(port)) |
714 | return; | 718 | return; |
715 | if (team->ops.port_disabled) | 719 | if (team->ops.port_disabled) |
716 | team->ops.port_disabled(team, port); | 720 | team->ops.port_disabled(team, port); |
717 | hlist_del_rcu(&port->hlist); | 721 | hlist_del_rcu(&port->hlist); |
718 | __reconstruct_port_hlist(team, rm_index); | 722 | __reconstruct_port_hlist(team, port->index); |
719 | team->en_port_count--; | ||
720 | port->index = -1; | 723 | port->index = -1; |
724 | __team_adjust_ops(team, team->en_port_count - 1); | ||
725 | /* | ||
726 | * Wait until readers see adjusted ops. This ensures that | ||
727 | * readers never see team->en_port_count == 0 | ||
728 | */ | ||
729 | synchronize_rcu(); | ||
730 | team->en_port_count--; | ||
721 | } | 731 | } |
722 | 732 | ||
723 | #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ | 733 | #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ |
@@ -874,7 +884,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev) | |||
874 | port->index = -1; | 884 | port->index = -1; |
875 | team_port_enable(team, port); | 885 | team_port_enable(team, port); |
876 | list_add_tail_rcu(&port->list, &team->port_list); | 886 | list_add_tail_rcu(&port->list, &team->port_list); |
877 | team_adjust_ops(team); | ||
878 | __team_compute_features(team); | 887 | __team_compute_features(team); |
879 | __team_port_change_check(port, !!netif_carrier_ok(port_dev)); | 888 | __team_port_change_check(port, !!netif_carrier_ok(port_dev)); |
880 | __team_options_change_check(team); | 889 | __team_options_change_check(team); |
@@ -928,7 +937,6 @@ static int team_port_del(struct team *team, struct net_device *port_dev) | |||
928 | __team_port_change_check(port, false); | 937 | __team_port_change_check(port, false); |
929 | team_port_disable(team, port); | 938 | team_port_disable(team, port); |
930 | list_del_rcu(&port->list); | 939 | list_del_rcu(&port->list); |
931 | team_adjust_ops(team); | ||
932 | netdev_rx_handler_unregister(port_dev); | 940 | netdev_rx_handler_unregister(port_dev); |
933 | netdev_set_master(port_dev, NULL); | 941 | netdev_set_master(port_dev, NULL); |
934 | vlan_vids_del_by_dev(port_dev, dev); | 942 | vlan_vids_del_by_dev(port_dev, dev); |