| From b39962950339912978484cdac50069258545d753 Mon Sep 17 00:00:00 2001 |
| From: Dmitry Fomichev <dmitry.fomichev@wdc.com> |
| Date: Mon, 23 Dec 2019 17:05:46 -0800 |
| Subject: dm zoned: support zone sizes smaller than 128MiB |
| |
| From: Dmitry Fomichev <dmitry.fomichev@wdc.com> |
| |
| commit b39962950339912978484cdac50069258545d753 upstream. |
| |
| dm-zoned is observed to log failed kernel assertions and not work |
| correctly when operating against a device with a zone size smaller |
| than 128MiB (e.g. 32768 bits per 4K block). The reason is that the |
| bitmap size per zone is calculated as zero with such a small zone |
| size. Fix this problem and also make the code related to zone bitmap |
| management be able to handle per zone bitmaps smaller than a single |
| block. |
| |
| A dm-zoned-tools patch is required to properly format dm-zoned devices |
| with zone sizes smaller than 128MiB. |
| |
| Fixes: 3b1a94c88b79 ("dm zoned: drive-managed zoned block device target") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Dmitry Fomichev <dmitry.fomichev@wdc.com> |
| Reviewed-by: Damien Le Moal <damien.lemoal@wdc.com> |
| Signed-off-by: Mike Snitzer <snitzer@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/md/dm-zoned-metadata.c | 23 ++++++++++++++--------- |
| 1 file changed, 14 insertions(+), 9 deletions(-) |
| |
| --- a/drivers/md/dm-zoned-metadata.c |
| +++ b/drivers/md/dm-zoned-metadata.c |
| @@ -132,6 +132,7 @@ struct dmz_metadata { |
| |
| sector_t zone_bitmap_size; |
| unsigned int zone_nr_bitmap_blocks; |
| + unsigned int zone_bits_per_mblk; |
| |
| unsigned int nr_bitmap_blocks; |
| unsigned int nr_map_blocks; |
| @@ -1165,7 +1166,10 @@ static int dmz_init_zones(struct dmz_met |
| |
| /* Init */ |
| zmd->zone_bitmap_size = dev->zone_nr_blocks >> 3; |
| - zmd->zone_nr_bitmap_blocks = zmd->zone_bitmap_size >> DMZ_BLOCK_SHIFT; |
| + zmd->zone_nr_bitmap_blocks = |
| + max_t(sector_t, 1, zmd->zone_bitmap_size >> DMZ_BLOCK_SHIFT); |
| + zmd->zone_bits_per_mblk = min_t(sector_t, dev->zone_nr_blocks, |
| + DMZ_BLOCK_SIZE_BITS); |
| |
| /* Allocate zone array */ |
| zmd->zones = kcalloc(dev->nr_zones, sizeof(struct dm_zone), GFP_KERNEL); |
| @@ -1982,7 +1986,7 @@ int dmz_copy_valid_blocks(struct dmz_met |
| dmz_release_mblock(zmd, to_mblk); |
| dmz_release_mblock(zmd, from_mblk); |
| |
| - chunk_block += DMZ_BLOCK_SIZE_BITS; |
| + chunk_block += zmd->zone_bits_per_mblk; |
| } |
| |
| to_zone->weight = from_zone->weight; |
| @@ -2043,7 +2047,7 @@ int dmz_validate_blocks(struct dmz_metad |
| |
| /* Set bits */ |
| bit = chunk_block & DMZ_BLOCK_MASK_BITS; |
| - nr_bits = min(nr_blocks, DMZ_BLOCK_SIZE_BITS - bit); |
| + nr_bits = min(nr_blocks, zmd->zone_bits_per_mblk - bit); |
| |
| count = dmz_set_bits((unsigned long *)mblk->data, bit, nr_bits); |
| if (count) { |
| @@ -2122,7 +2126,7 @@ int dmz_invalidate_blocks(struct dmz_met |
| |
| /* Clear bits */ |
| bit = chunk_block & DMZ_BLOCK_MASK_BITS; |
| - nr_bits = min(nr_blocks, DMZ_BLOCK_SIZE_BITS - bit); |
| + nr_bits = min(nr_blocks, zmd->zone_bits_per_mblk - bit); |
| |
| count = dmz_clear_bits((unsigned long *)mblk->data, |
| bit, nr_bits); |
| @@ -2182,6 +2186,7 @@ static int dmz_to_next_set_block(struct |
| { |
| struct dmz_mblock *mblk; |
| unsigned int bit, set_bit, nr_bits; |
| + unsigned int zone_bits = zmd->zone_bits_per_mblk; |
| unsigned long *bitmap; |
| int n = 0; |
| |
| @@ -2196,15 +2201,15 @@ static int dmz_to_next_set_block(struct |
| /* Get offset */ |
| bitmap = (unsigned long *) mblk->data; |
| bit = chunk_block & DMZ_BLOCK_MASK_BITS; |
| - nr_bits = min(nr_blocks, DMZ_BLOCK_SIZE_BITS - bit); |
| + nr_bits = min(nr_blocks, zone_bits - bit); |
| if (set) |
| - set_bit = find_next_bit(bitmap, DMZ_BLOCK_SIZE_BITS, bit); |
| + set_bit = find_next_bit(bitmap, zone_bits, bit); |
| else |
| - set_bit = find_next_zero_bit(bitmap, DMZ_BLOCK_SIZE_BITS, bit); |
| + set_bit = find_next_zero_bit(bitmap, zone_bits, bit); |
| dmz_release_mblock(zmd, mblk); |
| |
| n += set_bit - bit; |
| - if (set_bit < DMZ_BLOCK_SIZE_BITS) |
| + if (set_bit < zone_bits) |
| break; |
| |
| nr_blocks -= nr_bits; |
| @@ -2307,7 +2312,7 @@ static void dmz_get_zone_weight(struct d |
| /* Count bits in this block */ |
| bitmap = mblk->data; |
| bit = chunk_block & DMZ_BLOCK_MASK_BITS; |
| - nr_bits = min(nr_blocks, DMZ_BLOCK_SIZE_BITS - bit); |
| + nr_bits = min(nr_blocks, zmd->zone_bits_per_mblk - bit); |
| n += dmz_count_bits(bitmap, bit, nr_bits); |
| |
| dmz_release_mblock(zmd, mblk); |