aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block/drbd/drbd_actlog.c
diff options
context:
space:
mode:
authorLars Ellenberg <lars.ellenberg@linbit.com>2011-11-02 11:29:45 -0400
committerPhilipp Reisner <philipp.reisner@linbit.com>2012-05-09 09:15:58 -0400
commit763eb63625a625e4d160cbb4cce2bcdb40141b97 (patch)
tree7399533f422da35884c7a67b9972b23757890256 /drivers/block/drbd/drbd_actlog.c
parente89868a0927cfb8a3f535c938e5d6dd7edc6353c (diff)
drbd: fix potential spinlock deadlock
drbd_try_clear_on_disk_bm() has a sanity check for the number of blocks left to be resynced (rs_left) in the current resync extent. If it detects a mismatch, it complains, and forces a disconnect using drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); Unfortunately, this may be called while holding the req_lock, and drbd_force_state() want's to aquire that lock itself. Deadlock. Don't force a disconnect, but fix up rs_left by recounting and reassigning the number of dirty blocks in that extent. Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block/drbd/drbd_actlog.c')
-rw-r--r--drivers/block/drbd/drbd_actlog.c20
1 files changed, 12 insertions, 8 deletions
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index 3d7c2153daca..601ad9ef0437 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -711,16 +711,20 @@ static void drbd_try_clear_on_disk_bm(struct drbd_conf *mdev, sector_t sector,
711 else 711 else
712 ext->rs_failed += count; 712 ext->rs_failed += count;
713 if (ext->rs_left < ext->rs_failed) { 713 if (ext->rs_left < ext->rs_failed) {
714 dev_err(DEV, "BAD! sector=%llus enr=%u rs_left=%d " 714 dev_warn(DEV, "BAD! sector=%llus enr=%u rs_left=%d "
715 "rs_failed=%d count=%d\n", 715 "rs_failed=%d count=%d cstate=%s\n",
716 (unsigned long long)sector, 716 (unsigned long long)sector,
717 ext->lce.lc_number, ext->rs_left, 717 ext->lce.lc_number, ext->rs_left,
718 ext->rs_failed, count); 718 ext->rs_failed, count,
719 dump_stack(); 719 drbd_conn_str(mdev->state.conn));
720 720
721 lc_put(mdev->resync, &ext->lce); 721 /* We don't expect to be able to clear more bits
722 drbd_force_state(mdev, NS(conn, C_DISCONNECTING)); 722 * than have been set when we originally counted
723 return; 723 * the set bits to cache that value in ext->rs_left.
724 * Whatever the reason (disconnect during resync,
725 * delayed local completion of an application write),
726 * try to fix it up by recounting here. */
727 ext->rs_left = drbd_bm_e_weight(mdev, enr);
724 } 728 }
725 } else { 729 } else {
726 /* Normally this element should be in the cache, 730 /* Normally this element should be in the cache,