diff options
| author | Jay Vosburgh <fubar@us.ibm.com> | 2011-10-28 11:42:50 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2011-10-30 03:13:14 -0400 |
| commit | e6d265e8504ab4a3368b8645d318b344ee88b280 (patch) | |
| tree | 6fd2bc16819bae491e56be2658fc3298b7efa92c | |
| parent | 10ee0faed92c8af4baebd633372136a6608a41ea (diff) | |
bonding: eliminate bond_close race conditions
This patch resolves two sets of race conditions.
Mitsuo Hayasaka <mitsuo.hayasaka.hu@hitachi.com> reported the
first, as follows:
The bond_close() calls cancel_delayed_work() to cancel delayed works.
It, however, cannot cancel works that were already queued in workqueue.
The bond_open() initializes work->data, and proccess_one_work() refers
get_work_cwq(work)->wq->flags. The get_work_cwq() returns NULL when
work->data has been initialized. Thus, a panic occurs.
He included a patch that converted the cancel_delayed_work calls
in bond_close to flush_delayed_work_sync, which eliminated the above
problem.
His patch is incorporated, at least in principle, into this
patch. In this patch, we use cancel_delayed_work_sync in place of
flush_delayed_work_sync, and also convert bond_uninit in addition to
bond_close.
This conversion to _sync, however, opens new races between
bond_close and three periodically executing workqueue functions:
bond_mii_monitor, bond_alb_monitor and bond_activebackup_arp_mon.
The race occurs because bond_close and bond_uninit are always
called with RTNL held, and these workqueue functions may acquire RTNL to
perform failover-related activities. If bond_close or bond_uninit is
waiting in cancel_delayed_work_sync, deadlock occurs.
These deadlocks are resolved by having the workqueue functions
acquire RTNL conditionally. If the rtnl_trylock() fails, the functions
reschedule and return immediately. For the cases that are attempting to
perform link failover, a delay of 1 is used; for the other cases, the
normal interval is used (as those activities are not as time critical).
Additionally, the bond_mii_monitor function now stores the delay
in a variable (mimicing the structure of activebackup_arp_mon).
Lastly, all of the above renders the kill_timers sentinel moot,
and therefore it has been removed.
Tested-by: Mitsuo Hayasaka <mitsuo.hayasaka.hu@hitachi.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | drivers/net/bonding/bond_3ad.c | 8 | ||||
| -rw-r--r-- | drivers/net/bonding/bond_alb.c | 16 | ||||
| -rw-r--r-- | drivers/net/bonding/bond_main.c | 96 | ||||
| -rw-r--r-- | drivers/net/bonding/bonding.h | 1 |
4 files changed, 61 insertions, 60 deletions
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index b33c099d65a4..0ae0d7c54ccf 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c | |||
| @@ -2110,9 +2110,6 @@ void bond_3ad_state_machine_handler(struct work_struct *work) | |||
| 2110 | 2110 | ||
| 2111 | read_lock(&bond->lock); | 2111 | read_lock(&bond->lock); |
| 2112 | 2112 | ||
| 2113 | if (bond->kill_timers) | ||
| 2114 | goto out; | ||
| 2115 | |||
| 2116 | //check if there are any slaves | 2113 | //check if there are any slaves |
| 2117 | if (bond->slave_cnt == 0) | 2114 | if (bond->slave_cnt == 0) |
| 2118 | goto re_arm; | 2115 | goto re_arm; |
| @@ -2161,9 +2158,8 @@ void bond_3ad_state_machine_handler(struct work_struct *work) | |||
| 2161 | } | 2158 | } |
| 2162 | 2159 | ||
| 2163 | re_arm: | 2160 | re_arm: |
| 2164 | if (!bond->kill_timers) | 2161 | queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks); |
| 2165 | queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks); | 2162 | |
| 2166 | out: | ||
| 2167 | read_unlock(&bond->lock); | 2163 | read_unlock(&bond->lock); |
| 2168 | } | 2164 | } |
| 2169 | 2165 | ||
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index d4fbd2e62616..106b88a04738 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c | |||
| @@ -1343,10 +1343,6 @@ void bond_alb_monitor(struct work_struct *work) | |||
| 1343 | 1343 | ||
| 1344 | read_lock(&bond->lock); | 1344 | read_lock(&bond->lock); |
| 1345 | 1345 | ||
| 1346 | if (bond->kill_timers) { | ||
| 1347 | goto out; | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | if (bond->slave_cnt == 0) { | 1346 | if (bond->slave_cnt == 0) { |
| 1351 | bond_info->tx_rebalance_counter = 0; | 1347 | bond_info->tx_rebalance_counter = 0; |
| 1352 | bond_info->lp_counter = 0; | 1348 | bond_info->lp_counter = 0; |
| @@ -1401,10 +1397,13 @@ void bond_alb_monitor(struct work_struct *work) | |||
| 1401 | 1397 | ||
| 1402 | /* | 1398 | /* |
| 1403 | * dev_set_promiscuity requires rtnl and | 1399 | * dev_set_promiscuity requires rtnl and |
| 1404 | * nothing else. | 1400 | * nothing else. Avoid race with bond_close. |
| 1405 | */ | 1401 | */ |
| 1406 | read_unlock(&bond->lock); | 1402 | read_unlock(&bond->lock); |
| 1407 | rtnl_lock(); | 1403 | if (!rtnl_trylock()) { |
| 1404 | read_lock(&bond->lock); | ||
| 1405 | goto re_arm; | ||
| 1406 | } | ||
| 1408 | 1407 | ||
| 1409 | bond_info->rlb_promisc_timeout_counter = 0; | 1408 | bond_info->rlb_promisc_timeout_counter = 0; |
| 1410 | 1409 | ||
| @@ -1440,9 +1439,8 @@ void bond_alb_monitor(struct work_struct *work) | |||
| 1440 | } | 1439 | } |
| 1441 | 1440 | ||
| 1442 | re_arm: | 1441 | re_arm: |
| 1443 | if (!bond->kill_timers) | 1442 | queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks); |
| 1444 | queue_delayed_work(bond->wq, &bond->alb_work, alb_delta_in_ticks); | 1443 | |
| 1445 | out: | ||
| 1446 | read_unlock(&bond->lock); | 1444 | read_unlock(&bond->lock); |
| 1447 | } | 1445 | } |
| 1448 | 1446 | ||
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c5944f1a4f9d..c34cc1e7c6f6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c | |||
| @@ -773,9 +773,6 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) | |||
| 773 | 773 | ||
| 774 | read_lock(&bond->lock); | 774 | read_lock(&bond->lock); |
| 775 | 775 | ||
| 776 | if (bond->kill_timers) | ||
| 777 | goto out; | ||
| 778 | |||
| 779 | /* rejoin all groups on bond device */ | 776 | /* rejoin all groups on bond device */ |
| 780 | __bond_resend_igmp_join_requests(bond->dev); | 777 | __bond_resend_igmp_join_requests(bond->dev); |
| 781 | 778 | ||
| @@ -789,9 +786,9 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) | |||
| 789 | __bond_resend_igmp_join_requests(vlan_dev); | 786 | __bond_resend_igmp_join_requests(vlan_dev); |
| 790 | } | 787 | } |
| 791 | 788 | ||
| 792 | if ((--bond->igmp_retrans > 0) && !bond->kill_timers) | 789 | if (--bond->igmp_retrans > 0) |
| 793 | queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5); | 790 | queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5); |
| 794 | out: | 791 | |
| 795 | read_unlock(&bond->lock); | 792 | read_unlock(&bond->lock); |
| 796 | } | 793 | } |
| 797 | 794 | ||
| @@ -2517,10 +2514,11 @@ void bond_mii_monitor(struct work_struct *work) | |||
| 2517 | struct bonding *bond = container_of(work, struct bonding, | 2514 | struct bonding *bond = container_of(work, struct bonding, |
| 2518 | mii_work.work); | 2515 | mii_work.work); |
| 2519 | bool should_notify_peers = false; | 2516 | bool should_notify_peers = false; |
| 2517 | unsigned long delay; | ||
| 2520 | 2518 | ||
| 2521 | read_lock(&bond->lock); | 2519 | read_lock(&bond->lock); |
| 2522 | if (bond->kill_timers) | 2520 | |
| 2523 | goto out; | 2521 | delay = msecs_to_jiffies(bond->params.miimon); |
| 2524 | 2522 | ||
| 2525 | if (bond->slave_cnt == 0) | 2523 | if (bond->slave_cnt == 0) |
| 2526 | goto re_arm; | 2524 | goto re_arm; |
| @@ -2529,7 +2527,15 @@ void bond_mii_monitor(struct work_struct *work) | |||
| 2529 | 2527 | ||
| 2530 | if (bond_miimon_inspect(bond)) { | 2528 | if (bond_miimon_inspect(bond)) { |
| 2531 | read_unlock(&bond->lock); | 2529 | read_unlock(&bond->lock); |
| 2532 | rtnl_lock(); | 2530 | |
| 2531 | /* Race avoidance with bond_close cancel of workqueue */ | ||
| 2532 | if (!rtnl_trylock()) { | ||
| 2533 | read_lock(&bond->lock); | ||
| 2534 | delay = 1; | ||
| 2535 | should_notify_peers = false; | ||
| 2536 | goto re_arm; | ||
| 2537 | } | ||
| 2538 | |||
| 2533 | read_lock(&bond->lock); | 2539 | read_lock(&bond->lock); |
| 2534 | 2540 | ||
| 2535 | bond_miimon_commit(bond); | 2541 | bond_miimon_commit(bond); |
| @@ -2540,14 +2546,18 @@ void bond_mii_monitor(struct work_struct *work) | |||
| 2540 | } | 2546 | } |
| 2541 | 2547 | ||
| 2542 | re_arm: | 2548 | re_arm: |
| 2543 | if (bond->params.miimon && !bond->kill_timers) | 2549 | if (bond->params.miimon) |
| 2544 | queue_delayed_work(bond->wq, &bond->mii_work, | 2550 | queue_delayed_work(bond->wq, &bond->mii_work, delay); |
| 2545 | msecs_to_jiffies(bond->params.miimon)); | 2551 | |
| 2546 | out: | ||
| 2547 | read_unlock(&bond->lock); | 2552 | read_unlock(&bond->lock); |
| 2548 | 2553 | ||
| 2549 | if (should_notify_peers) { | 2554 | if (should_notify_peers) { |
| 2550 | rtnl_lock(); | 2555 | if (!rtnl_trylock()) { |
| 2556 | read_lock(&bond->lock); | ||
| 2557 | bond->send_peer_notif++; | ||
| 2558 | read_unlock(&bond->lock); | ||
| 2559 | return; | ||
| 2560 | } | ||
| 2551 | netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS); | 2561 | netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS); |
| 2552 | rtnl_unlock(); | 2562 | rtnl_unlock(); |
| 2553 | } | 2563 | } |
| @@ -2789,9 +2799,6 @@ void bond_loadbalance_arp_mon(struct work_struct *work) | |||
| 2789 | 2799 | ||
| 2790 | delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); | 2800 | delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); |
| 2791 | 2801 | ||
| 2792 | if (bond->kill_timers) | ||
| 2793 | goto out; | ||
| 2794 | |||
| 2795 | if (bond->slave_cnt == 0) | 2802 | if (bond->slave_cnt == 0) |
| 2796 | goto re_arm; | 2803 | goto re_arm; |
| 2797 | 2804 | ||
| @@ -2888,9 +2895,9 @@ void bond_loadbalance_arp_mon(struct work_struct *work) | |||
| 2888 | } | 2895 | } |
| 2889 | 2896 | ||
| 2890 | re_arm: | 2897 | re_arm: |
| 2891 | if (bond->params.arp_interval && !bond->kill_timers) | 2898 | if (bond->params.arp_interval) |
| 2892 | queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks); | 2899 | queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks); |
| 2893 | out: | 2900 | |
| 2894 | read_unlock(&bond->lock); | 2901 | read_unlock(&bond->lock); |
| 2895 | } | 2902 | } |
| 2896 | 2903 | ||
| @@ -3131,9 +3138,6 @@ void bond_activebackup_arp_mon(struct work_struct *work) | |||
| 3131 | 3138 | ||
| 3132 | read_lock(&bond->lock); | 3139 | read_lock(&bond->lock); |
| 3133 | 3140 | ||
| 3134 | if (bond->kill_timers) | ||
| 3135 | goto out; | ||
| 3136 | |||
| 3137 | delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); | 3141 | delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); |
| 3138 | 3142 | ||
| 3139 | if (bond->slave_cnt == 0) | 3143 | if (bond->slave_cnt == 0) |
| @@ -3143,7 +3147,15 @@ void bond_activebackup_arp_mon(struct work_struct *work) | |||
| 3143 | 3147 | ||
| 3144 | if (bond_ab_arp_inspect(bond, delta_in_ticks)) { | 3148 | if (bond_ab_arp_inspect(bond, delta_in_ticks)) { |
| 3145 | read_unlock(&bond->lock); | 3149 | read_unlock(&bond->lock); |
| 3146 | rtnl_lock(); | 3150 | |
| 3151 | /* Race avoidance with bond_close flush of workqueue */ | ||
| 3152 | if (!rtnl_trylock()) { | ||
| 3153 | read_lock(&bond->lock); | ||
| 3154 | delta_in_ticks = 1; | ||
| 3155 | should_notify_peers = false; | ||
| 3156 | goto re_arm; | ||
| 3157 | } | ||
| 3158 | |||
| 3147 | read_lock(&bond->lock); | 3159 | read_lock(&bond->lock); |
| 3148 | 3160 | ||
| 3149 | bond_ab_arp_commit(bond, delta_in_ticks); | 3161 | bond_ab_arp_commit(bond, delta_in_ticks); |
| @@ -3156,13 +3168,18 @@ void bond_activebackup_arp_mon(struct work_struct *work) | |||
| 3156 | bond_ab_arp_probe(bond); | 3168 | bond_ab_arp_probe(bond); |
| 3157 | 3169 | ||
| 3158 | re_arm: | 3170 | re_arm: |
| 3159 | if (bond->params.arp_interval && !bond->kill_timers) | 3171 | if (bond->params.arp_interval) |
| 3160 | queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks); | 3172 | queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks); |
| 3161 | out: | 3173 | |
| 3162 | read_unlock(&bond->lock); | 3174 | read_unlock(&bond->lock); |
| 3163 | 3175 | ||
| 3164 | if (should_notify_peers) { | 3176 | if (should_notify_peers) { |
| 3165 | rtnl_lock(); | 3177 | if (!rtnl_trylock()) { |
| 3178 | read_lock(&bond->lock); | ||
| 3179 | bond->send_peer_notif++; | ||
| 3180 | read_unlock(&bond->lock); | ||
| 3181 | return; | ||
| 3182 | } | ||
| 3166 | netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS); | 3183 | netdev_bonding_change(bond->dev, NETDEV_NOTIFY_PEERS); |
| 3167 | rtnl_unlock(); | 3184 | rtnl_unlock(); |
| 3168 | } | 3185 | } |
| @@ -3424,8 +3441,6 @@ static int bond_open(struct net_device *bond_dev) | |||
| 3424 | struct slave *slave; | 3441 | struct slave *slave; |
| 3425 | int i; | 3442 | int i; |
| 3426 | 3443 | ||
| 3427 | bond->kill_timers = 0; | ||
| 3428 | |||
| 3429 | /* reset slave->backup and slave->inactive */ | 3444 | /* reset slave->backup and slave->inactive */ |
| 3430 | read_lock(&bond->lock); | 3445 | read_lock(&bond->lock); |
| 3431 | if (bond->slave_cnt > 0) { | 3446 | if (bond->slave_cnt > 0) { |
| @@ -3494,33 +3509,30 @@ static int bond_close(struct net_device *bond_dev) | |||
| 3494 | 3509 | ||
| 3495 | bond->send_peer_notif = 0; | 3510 | bond->send_peer_notif = 0; |
| 3496 | 3511 | ||
| 3497 | /* signal timers not to re-arm */ | ||
| 3498 | bond->kill_timers = 1; | ||
| 3499 | |||
| 3500 | write_unlock_bh(&bond->lock); | 3512 | write_unlock_bh(&bond->lock); |
| 3501 | 3513 | ||
| 3502 | if (bond->params.miimon) { /* link check interval, in milliseconds. */ | 3514 | if (bond->params.miimon) { /* link check interval, in milliseconds. */ |
| 3503 | cancel_delayed_work(&bond->mii_work); | 3515 | cancel_delayed_work_sync(&bond->mii_work); |
| 3504 | } | 3516 | } |
| 3505 | 3517 | ||
| 3506 | if (bond->params.arp_interval) { /* arp interval, in milliseconds. */ | 3518 | if (bond->params.arp_interval) { /* arp interval, in milliseconds. */ |
| 3507 | cancel_delayed_work(&bond->arp_work); | 3519 | cancel_delayed_work_sync(&bond->arp_work); |
| 3508 | } | 3520 | } |
| 3509 | 3521 | ||
| 3510 | switch (bond->params.mode) { | 3522 | switch (bond->params.mode) { |
| 3511 | case BOND_MODE_8023AD: | 3523 | case BOND_MODE_8023AD: |
| 3512 | cancel_delayed_work(&bond->ad_work); | 3524 | cancel_delayed_work_sync(&bond->ad_work); |
| 3513 | break; | 3525 | break; |
| 3514 | case BOND_MODE_TLB: | 3526 | case BOND_MODE_TLB: |
| 3515 | case BOND_MODE_ALB: | 3527 | case BOND_MODE_ALB: |
| 3516 | cancel_delayed_work(&bond->alb_work); | 3528 | cancel_delayed_work_sync(&bond->alb_work); |
| 3517 | break; | 3529 | break; |
| 3518 | default: | 3530 | default: |
| 3519 | break; | 3531 | break; |
| 3520 | } | 3532 | } |
| 3521 | 3533 | ||
| 3522 | if (delayed_work_pending(&bond->mcast_work)) | 3534 | if (delayed_work_pending(&bond->mcast_work)) |
| 3523 | cancel_delayed_work(&bond->mcast_work); | 3535 | cancel_delayed_work_sync(&bond->mcast_work); |
| 3524 | 3536 | ||
| 3525 | if (bond_is_lb(bond)) { | 3537 | if (bond_is_lb(bond)) { |
| 3526 | /* Must be called only after all | 3538 | /* Must be called only after all |
| @@ -4367,26 +4379,22 @@ static void bond_setup(struct net_device *bond_dev) | |||
| 4367 | 4379 | ||
| 4368 | static void bond_work_cancel_all(struct bonding *bond) | 4380 | static void bond_work_cancel_all(struct bonding *bond) |
| 4369 | { | 4381 | { |
| 4370 | write_lock_bh(&bond->lock); | ||
| 4371 | bond->kill_timers = 1; | ||
| 4372 | write_unlock_bh(&bond->lock); | ||
| 4373 | |||
| 4374 | if (bond->params.miimon && delayed_work_pending(&bond->mii_work)) | 4382 | if (bond->params.miimon && delayed_work_pending(&bond->mii_work)) |
| 4375 | cancel_delayed_work(&bond->mii_work); | 4383 | cancel_delayed_work_sync(&bond->mii_work); |
| 4376 | 4384 | ||
| 4377 | if (bond->params.arp_interval && delayed_work_pending(&bond->arp_work)) | 4385 | if (bond->params.arp_interval && delayed_work_pending(&bond->arp_work)) |
| 4378 | cancel_delayed_work(&bond->arp_work); | 4386 | cancel_delayed_work_sync(&bond->arp_work); |
| 4379 | 4387 | ||
| 4380 | if (bond->params.mode == BOND_MODE_ALB && | 4388 | if (bond->params.mode == BOND_MODE_ALB && |
| 4381 | delayed_work_pending(&bond->alb_work)) | 4389 | delayed_work_pending(&bond->alb_work)) |
| 4382 | cancel_delayed_work(&bond->alb_work); | 4390 | cancel_delayed_work_sync(&bond->alb_work); |
| 4383 | 4391 | ||
| 4384 | if (bond->params.mode == BOND_MODE_8023AD && | 4392 | if (bond->params.mode == BOND_MODE_8023AD && |
| 4385 | delayed_work_pending(&bond->ad_work)) | 4393 | delayed_work_pending(&bond->ad_work)) |
| 4386 | cancel_delayed_work(&bond->ad_work); | 4394 | cancel_delayed_work_sync(&bond->ad_work); |
| 4387 | 4395 | ||
| 4388 | if (delayed_work_pending(&bond->mcast_work)) | 4396 | if (delayed_work_pending(&bond->mcast_work)) |
| 4389 | cancel_delayed_work(&bond->mcast_work); | 4397 | cancel_delayed_work_sync(&bond->mcast_work); |
| 4390 | } | 4398 | } |
| 4391 | 4399 | ||
| 4392 | /* | 4400 | /* |
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 82fec5fc75d7..1aecc37e5b4d 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h | |||
| @@ -222,7 +222,6 @@ struct bonding { | |||
| 222 | struct slave *); | 222 | struct slave *); |
| 223 | rwlock_t lock; | 223 | rwlock_t lock; |
| 224 | rwlock_t curr_slave_lock; | 224 | rwlock_t curr_slave_lock; |
| 225 | s8 kill_timers; | ||
| 226 | u8 send_peer_notif; | 225 | u8 send_peer_notif; |
| 227 | s8 setup_by_slave; | 226 | s8 setup_by_slave; |
| 228 | s8 igmp_retrans; | 227 | s8 igmp_retrans; |
