diff options
author | Alasdair G Kergon <agk@redhat.com> | 2005-11-22 00:32:34 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-22 12:14:31 -0500 |
commit | 0e56822d30184d0da35a6ecc51f38c4ceb457a80 (patch) | |
tree | a9ee18ce0e724f16dde3c5c97af67fb3a92701f7 /drivers/md/dm-log.c | |
parent | c4cc66351a24da5feec298be2da59a85f68dd3ea (diff) |
[PATCH] device-mapper: mirror log bitset fix
The linux bitset operators (test_bit, set_bit etc) work on arrays of "unsigned
long". dm-log uses such bitsets but treats them as arrays of uint32_t, only
allocating and zeroing a multiple of 4 bytes (as 'clean_bits' is a uint32_t).
The patch below fixes this problem.
The problem is specific to 64-bit big endian machines such as s390x or ppc-64
and can prevent pvmove terminating.
In the simplest case, if "region_count" were (say) 30, then
bitset_size (below) would be 4 and bitset_uint32_count would be 1.
Thus the memory for this butset, after allocation and zeroing would
be
0 0 0 0 X X X X
On a bigendian 64bit machine, bit 0 for this bitset is in the 8th
byte! (and every bit that dm-log would use would be in the X area).
0 0 0 0 X X X X
^
here
which hasn't been cleared properly.
As the dm-raid1 code only syncs and counts regions which have a 0 in the
'sync_bits' bitset, and only finishes when it has counted high enough, a large
number of 1's among those 'X's will cause the sync to not complete.
It is worth noting that the code uses the same bitsets for in-memory and
on-disk logs. As these bitsets are host-endian and host-sized, this means
that they cannot safely be moved between computers with
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/md/dm-log.c')
-rw-r--r-- | drivers/md/dm-log.c | 4 |
1 files changed, 2 insertions, 2 deletions
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index e110655eabdb..a76349cb10a5 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c | |||
@@ -333,10 +333,10 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti, | |||
333 | lc->sync = sync; | 333 | lc->sync = sync; |
334 | 334 | ||
335 | /* | 335 | /* |
336 | * Work out how many words we need to hold the bitset. | 336 | * Work out how many "unsigned long"s we need to hold the bitset. |
337 | */ | 337 | */ |
338 | bitset_size = dm_round_up(region_count, | 338 | bitset_size = dm_round_up(region_count, |
339 | sizeof(*lc->clean_bits) << BYTE_SHIFT); | 339 | sizeof(unsigned long) << BYTE_SHIFT); |
340 | bitset_size >>= BYTE_SHIFT; | 340 | bitset_size >>= BYTE_SHIFT; |
341 | 341 | ||
342 | lc->bitset_uint32_count = bitset_size / 4; | 342 | lc->bitset_uint32_count = bitset_size / 4; |