Skip to content

Commit b0d11b4

Browse files
jpirkodavem330
authored andcommitted
team: avoid possible underflow of count_pending value for notify_peers and mcast_rejoin
This patch is fixing a race condition that may cause setting count_pending to -1, which results in unwanted big bulk of arp messages (in case of "notify peers"). Consider following scenario: count_pending == 2 CPU0 CPU1 team_notify_peers_work atomic_dec_and_test (dec count_pending to 1) schedule_delayed_work team_notify_peers atomic_add (adding 1 to count_pending) team_notify_peers_work atomic_dec_and_test (dec count_pending to 1) schedule_delayed_work team_notify_peers_work atomic_dec_and_test (dec count_pending to 0) schedule_delayed_work team_notify_peers_work atomic_dec_and_test (dec count_pending to -1) Fix this race by using atomic_dec_if_positive - that will prevent count_pending running under 0. Fixes: fc423ff ("team: add peer notification") Fixes: 492b200 ("team: add support for sending multicast rejoins") Signed-off-by: Jiri Pirko <jiri@resnulli.us> Signed-off-by: Jiri Benc <jbenc@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 1ba3980 commit b0d11b4

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

drivers/net/team/team.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,16 +629,22 @@ static int team_change_mode(struct team *team, const char *kind)
629629
static void team_notify_peers_work(struct work_struct *work)
630630
{
631631
struct team *team;
632+
int val;
632633

633634
team = container_of(work, struct team, notify_peers.dw.work);
634635

635636
if (!rtnl_trylock()) {
636637
schedule_delayed_work(&team->notify_peers.dw, 0);
637638
return;
638639
}
640+
val = atomic_dec_if_positive(&team->notify_peers.count_pending);
641+
if (val < 0) {
642+
rtnl_unlock();
643+
return;
644+
}
639645
call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev);
640646
rtnl_unlock();
641-
if (!atomic_dec_and_test(&team->notify_peers.count_pending))
647+
if (val)
642648
schedule_delayed_work(&team->notify_peers.dw,
643649
msecs_to_jiffies(team->notify_peers.interval));
644650
}
@@ -669,16 +675,22 @@ static void team_notify_peers_fini(struct team *team)
669675
static void team_mcast_rejoin_work(struct work_struct *work)
670676
{
671677
struct team *team;
678+
int val;
672679

673680
team = container_of(work, struct team, mcast_rejoin.dw.work);
674681

675682
if (!rtnl_trylock()) {
676683
schedule_delayed_work(&team->mcast_rejoin.dw, 0);
677684
return;
678685
}
686+
val = atomic_dec_if_positive(&team->mcast_rejoin.count_pending);
687+
if (val < 0) {
688+
rtnl_unlock();
689+
return;
690+
}
679691
call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev);
680692
rtnl_unlock();
681-
if (!atomic_dec_and_test(&team->mcast_rejoin.count_pending))
693+
if (val)
682694
schedule_delayed_work(&team->mcast_rejoin.dw,
683695
msecs_to_jiffies(team->mcast_rejoin.interval));
684696
}

0 commit comments

Comments
 (0)