diff options
author | Lars Ellenberg <lars.ellenberg@linbit.com> | 2010-12-15 17:21:39 -0500 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2011-03-10 05:36:38 -0500 |
commit | 7777a8ba1fc980e5edfe492ebf5a1676497b8db2 (patch) | |
tree | 9eb81d2bee5bf63a796ae4df0b9cf352b3f0360b /drivers/block/drbd/drbd_bitmap.c | |
parent | 1b881ef77537f1077482f9946a6a99b4e2dd54b2 (diff) |
drbd: bitmap: don't count unused bits (fix non-terminating resync)
We trusted the on-disk bitmap to have unused bits cleared.
In case that is not true for whatever reason,
and we take a code path where the unused bits don't get cleared
elsewhere (bm_clear_surplus is not called), we may miscount the bits,
and get confused during resync, waiting for bits to get cleared that we
don't even use: the resync process would not terminate.
Fix this by masking out unused bits in __bm_count_bits.
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_bitmap.c')
-rw-r--r-- | drivers/block/drbd/drbd_bitmap.c | 25 |
1 files changed, 22 insertions, 3 deletions
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c index 9390e9526786..c5361487cf47 100644 --- a/drivers/block/drbd/drbd_bitmap.c +++ b/drivers/block/drbd/drbd_bitmap.c | |||
@@ -376,9 +376,16 @@ static unsigned long __bm_count_bits(struct drbd_bitmap *b, const int swap_endia | |||
376 | unsigned long *p_addr, *bm, offset = 0; | 376 | unsigned long *p_addr, *bm, offset = 0; |
377 | unsigned long bits = 0; | 377 | unsigned long bits = 0; |
378 | unsigned long i, do_now; | 378 | unsigned long i, do_now; |
379 | unsigned long words; | ||
379 | 380 | ||
380 | while (offset < b->bm_words) { | 381 | /* due to 64bit alignment, the last long on a 32bit arch |
381 | i = do_now = min_t(size_t, b->bm_words-offset, LWPP); | 382 | * may be not used at all. The last used long will likely |
383 | * be only partially used, always. Don't count those bits, | ||
384 | * but mask them out. */ | ||
385 | words = (b->bm_bits + BITS_PER_LONG - 1) >> LN2_BPL; | ||
386 | |||
387 | while (offset < words) { | ||
388 | i = do_now = min_t(size_t, words-offset, LWPP); | ||
382 | p_addr = __bm_map_paddr(b, offset, KM_USER0); | 389 | p_addr = __bm_map_paddr(b, offset, KM_USER0); |
383 | bm = p_addr + MLPP(offset); | 390 | bm = p_addr + MLPP(offset); |
384 | while (i--) { | 391 | while (i--) { |
@@ -388,8 +395,20 @@ static unsigned long __bm_count_bits(struct drbd_bitmap *b, const int swap_endia | |||
388 | #endif | 395 | #endif |
389 | bits += hweight_long(*bm++); | 396 | bits += hweight_long(*bm++); |
390 | } | 397 | } |
391 | __bm_unmap(p_addr, KM_USER0); | ||
392 | offset += do_now; | 398 | offset += do_now; |
399 | if (offset == words) { | ||
400 | /* last word may only be partially used, | ||
401 | * see also bm_clear_surplus. */ | ||
402 | i = (1UL << (b->bm_bits & (BITS_PER_LONG-1))) -1; | ||
403 | if (i) { | ||
404 | bits -= hweight_long(p_addr[do_now-1] & ~i); | ||
405 | p_addr[do_now-1] &= i; | ||
406 | } | ||
407 | /* 32bit arch, may have an unused padding long */ | ||
408 | if (words != b->bm_words) | ||
409 | p_addr[do_now] = 0; | ||
410 | } | ||
411 | __bm_unmap(p_addr, KM_USER0); | ||
393 | cond_resched(); | 412 | cond_resched(); |
394 | } | 413 | } |
395 | 414 | ||