dm zoned: support zone sizes smaller than 128MiB
authorDmitry Fomichev <dmitry.fomichev@wdc.com>
Tue, 24 Dec 2019 01:05:46 +0000 (17:05 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 11 Feb 2020 12:35:26 +0000 (04:35 -0800)
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

index ac1179ca80d984d3191128925bfc35608badd710..5205cf9bbfd924b3632a27bf4a555594e540d9aa 100644 (file)
@@ -134,6 +134,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;
@@ -1167,7 +1168,10 @@ static int dmz_init_zones(struct dmz_metadata *zmd)
 
        /* 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);
@@ -1991,7 +1995,7 @@ int dmz_copy_valid_blocks(struct dmz_metadata *zmd, struct dm_zone *from_zone,
                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;
@@ -2052,7 +2056,7 @@ int dmz_validate_blocks(struct dmz_metadata *zmd, struct dm_zone *zone,
 
                /* 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) {
@@ -2131,7 +2135,7 @@ int dmz_invalidate_blocks(struct dmz_metadata *zmd, struct dm_zone *zone,
 
                /* 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);
@@ -2191,6 +2195,7 @@ static int dmz_to_next_set_block(struct dmz_metadata *zmd, struct dm_zone *zone,
 {
        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;
 
@@ -2205,15 +2210,15 @@ static int dmz_to_next_set_block(struct dmz_metadata *zmd, struct dm_zone *zone,
                /* 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;
@@ -2316,7 +2321,7 @@ static void dmz_get_zone_weight(struct dmz_metadata *zmd, struct dm_zone *zone)
                /* 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);