| From 7aedf75ff740a98f3683439449cd91c8662d03b2 Mon Sep 17 00:00:00 2001 |
| From: Damien Le Moal <damien.lemoal@wdc.com> |
| Date: Thu, 18 Apr 2019 18:03:07 +0900 |
| Subject: dm zoned: Fix zone report handling |
| |
| From: Damien Le Moal <damien.lemoal@wdc.com> |
| |
| commit 7aedf75ff740a98f3683439449cd91c8662d03b2 upstream. |
| |
| The function blkdev_report_zones() returns success even if no zone |
| information is reported (empty report). Empty zone reports can only |
| happen if the report start sector passed exceeds the device capacity. |
| The conditions for this to happen are either a bug in the caller code, |
| or, a change in the device that forced the low level driver to change |
| the device capacity to a value that is lower than the report start |
| sector. This situation includes a failed disk revalidation resulting in |
| the disk capacity being changed to 0. |
| |
| If this change happens while dm-zoned is in its initialization phase |
| executing dmz_init_zones(), this function may enter an infinite loop |
| and hang the system. To avoid this, add a check to disallow empty zone |
| reports and bail out early. Also fix the function dmz_update_zone() to |
| make sure that the report for the requested zone was correctly obtained. |
| |
| Fixes: 3b1a94c88b79 ("dm zoned: drive-managed zoned block device target") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com> |
| Reviewed-by: Shaun Tancheff <shaun@tancheff.com> |
| Signed-off-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 | 5 +++++ |
| 1 file changed, 5 insertions(+) |
| |
| --- a/drivers/md/dm-zoned-metadata.c |
| +++ b/drivers/md/dm-zoned-metadata.c |
| @@ -1169,6 +1169,9 @@ static int dmz_init_zones(struct dmz_met |
| goto out; |
| } |
| |
| + if (!nr_blkz) |
| + break; |
| + |
| /* Process report */ |
| for (i = 0; i < nr_blkz; i++) { |
| ret = dmz_init_zone(zmd, zone, &blkz[i]); |
| @@ -1204,6 +1207,8 @@ static int dmz_update_zone(struct dmz_me |
| /* Get zone information from disk */ |
| ret = blkdev_report_zones(zmd->dev->bdev, dmz_start_sect(zmd, zone), |
| &blkz, &nr_blkz, GFP_NOIO); |
| + if (!nr_blkz) |
| + ret = -EIO; |
| if (ret) { |
| dmz_dev_err(zmd->dev, "Get zone %u report failed", |
| dmz_id(zmd, zone)); |