]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 5 Jun 2018 10:20:34 +0000 (12:20 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 5 Jun 2018 10:20:34 +0000 (12:20 +0200)
added patches:
scsi-sd_zbc-avoid-that-resetting-a-zone-fails-sporadically.patch
scsi-sd_zbc-fix-potential-memory-leak.patch

queue-4.14/scsi-sd_zbc-avoid-that-resetting-a-zone-fails-sporadically.patch [new file with mode: 0644]
queue-4.14/scsi-sd_zbc-fix-potential-memory-leak.patch [new file with mode: 0644]
queue-4.14/series [new file with mode: 0644]
queue-4.16/series [new file with mode: 0644]

diff --git a/queue-4.14/scsi-sd_zbc-avoid-that-resetting-a-zone-fails-sporadically.patch b/queue-4.14/scsi-sd_zbc-avoid-that-resetting-a-zone-fails-sporadically.patch
new file mode 100644 (file)
index 0000000..b572c97
--- /dev/null
@@ -0,0 +1,219 @@
+From ccce20fc7968d546fb1e8e147bf5cdc8afc4278a Mon Sep 17 00:00:00 2001
+From: Bart Van Assche <bart.vanassche@wdc.com>
+Date: Mon, 16 Apr 2018 18:04:41 -0700
+Subject: scsi: sd_zbc: Avoid that resetting a zone fails sporadically
+
+From: Bart Van Assche <bart.vanassche@wdc.com>
+
+commit ccce20fc7968d546fb1e8e147bf5cdc8afc4278a upstream.
+
+Since SCSI scanning occurs asynchronously, since sd_revalidate_disk() is
+called from sd_probe_async() and since sd_revalidate_disk() calls
+sd_zbc_read_zones() it can happen that sd_zbc_read_zones() is called
+concurrently with blkdev_report_zones() and/or blkdev_reset_zones().  That can
+cause these functions to fail with -EIO because sd_zbc_read_zones() e.g. sets
+q->nr_zones to zero before restoring it to the actual value, even if no drive
+characteristics have changed.  Avoid that this can happen by making the
+following changes:
+
+- Protect the code that updates zone information with blk_queue_enter()
+  and blk_queue_exit().
+- Modify sd_zbc_setup_seq_zones_bitmap() and sd_zbc_setup() such that
+  these functions do not modify struct scsi_disk before all zone
+  information has been obtained.
+
+Note: since commit 055f6e18e08f ("block: Make q_usage_counter also track
+legacy requests"; kernel v4.15) the request queue freezing mechanism also
+affects legacy request queues.
+
+Fixes: 89d947561077 ("sd: Implement support for ZBC devices")
+Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
+Cc: Jens Axboe <axboe@kernel.dk>
+Cc: Damien Le Moal <damien.lemoal@wdc.com>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Hannes Reinecke <hare@suse.com>
+Cc: stable@vger.kernel.org # v4.16
+Reviewed-by: Damien Le Moal <damien.lemoal@wdc.com>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/scsi/sd_zbc.c |   98 ++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 63 insertions(+), 35 deletions(-)
+
+--- a/drivers/scsi/sd_zbc.c
++++ b/drivers/scsi/sd_zbc.c
+@@ -423,7 +423,16 @@ static int sd_zbc_check_capacity(struct
+ #define SD_ZBC_BUF_SIZE 131072
+-static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
++/**
++ * sd_zbc_check_zone_size - Check the device zone sizes
++ * @sdkp: Target disk
++ *
++ * Check that all zones of the device are equal. The last zone can however
++ * be smaller. The zone size must also be a power of two number of LBAs.
++ *
++ * Returns the zone size in bytes upon success or an error code upon failure.
++ */
++static s64 sd_zbc_check_zone_size(struct scsi_disk *sdkp)
+ {
+       u64 zone_blocks = 0;
+       sector_t block = 0;
+@@ -434,8 +443,6 @@ static int sd_zbc_check_zone_size(struct
+       int ret;
+       u8 same;
+-      sdkp->zone_blocks = 0;
+-
+       /* Get a buffer */
+       buf = kmalloc(SD_ZBC_BUF_SIZE, GFP_KERNEL);
+       if (!buf)
+@@ -470,16 +477,17 @@ static int sd_zbc_check_zone_size(struct
+               /* Parse zone descriptors */
+               while (rec < buf + buf_len) {
+-                      zone_blocks = get_unaligned_be64(&rec[8]);
+-                      if (sdkp->zone_blocks == 0) {
+-                              sdkp->zone_blocks = zone_blocks;
+-                      } else if (zone_blocks != sdkp->zone_blocks &&
+-                                 (block + zone_blocks < sdkp->capacity
+-                                  || zone_blocks > sdkp->zone_blocks)) {
++                      u64 this_zone_blocks = get_unaligned_be64(&rec[8]);
++
++                      if (zone_blocks == 0) {
++                              zone_blocks = this_zone_blocks;
++                      } else if (this_zone_blocks != zone_blocks &&
++                                 (block + this_zone_blocks < sdkp->capacity
++                                  || this_zone_blocks > zone_blocks)) {
+                               zone_blocks = 0;
+                               goto out;
+                       }
+-                      block += zone_blocks;
++                      block += this_zone_blocks;
+                       rec += 64;
+               }
+@@ -492,8 +500,6 @@ static int sd_zbc_check_zone_size(struct
+       } while (block < sdkp->capacity);
+-      zone_blocks = sdkp->zone_blocks;
+-
+ out:
+       if (!zone_blocks) {
+               if (sdkp->first_scan)
+@@ -513,8 +519,7 @@ out:
+                                 "Zone size too large\n");
+               ret = -ENODEV;
+       } else {
+-              sdkp->zone_blocks = zone_blocks;
+-              sdkp->zone_shift = ilog2(zone_blocks);
++              ret = zone_blocks;
+       }
+ out_free:
+@@ -523,23 +528,44 @@ out_free:
+       return ret;
+ }
+-static int sd_zbc_setup(struct scsi_disk *sdkp)
++static int sd_zbc_setup(struct scsi_disk *sdkp, u32 zone_blocks)
+ {
++      struct request_queue *q = sdkp->disk->queue;
++      u32 zone_shift = ilog2(zone_blocks);
++      u32 nr_zones;
+       /* chunk_sectors indicates the zone size */
+-      blk_queue_chunk_sectors(sdkp->disk->queue,
+-                      logical_to_sectors(sdkp->device, sdkp->zone_blocks));
+-      sdkp->zone_shift = ilog2(sdkp->zone_blocks);
+-      sdkp->nr_zones = sdkp->capacity >> sdkp->zone_shift;
+-      if (sdkp->capacity & (sdkp->zone_blocks - 1))
+-              sdkp->nr_zones++;
+-
+-      if (!sdkp->zones_wlock) {
+-              sdkp->zones_wlock = kcalloc(BITS_TO_LONGS(sdkp->nr_zones),
+-                                          sizeof(unsigned long),
+-                                          GFP_KERNEL);
+-              if (!sdkp->zones_wlock)
+-                      return -ENOMEM;
++      blk_queue_chunk_sectors(q,
++                      logical_to_sectors(sdkp->device, zone_blocks));
++      nr_zones = round_up(sdkp->capacity, zone_blocks) >> zone_shift;
++
++      /*
++       * Initialize the disk zone write lock bitmap if the number
++       * of zones changed.
++       */
++      if (nr_zones != sdkp->nr_zones) {
++              unsigned long *zones_wlock = NULL;
++
++              if (nr_zones) {
++                      zones_wlock = kcalloc(BITS_TO_LONGS(nr_zones),
++                                            sizeof(unsigned long),
++                                            GFP_KERNEL);
++                      if (!zones_wlock)
++                              return -ENOMEM;
++              }
++
++              blk_mq_freeze_queue(q);
++              sdkp->zone_blocks = zone_blocks;
++              sdkp->zone_shift = zone_shift;
++              sdkp->nr_zones = nr_zones;
++              swap(sdkp->zones_wlock, zones_wlock);
++              blk_mq_unfreeze_queue(q);
++
++              kfree(zones_wlock);
++
++              /* READ16/WRITE16 is mandatory for ZBC disks */
++              sdkp->device->use_16_for_rw = 1;
++              sdkp->device->use_10_for_rw = 0;
+       }
+       return 0;
+@@ -548,6 +574,7 @@ static int sd_zbc_setup(struct scsi_disk
+ int sd_zbc_read_zones(struct scsi_disk *sdkp,
+                     unsigned char *buf)
+ {
++      int64_t zone_blocks;
+       int ret;
+       if (!sd_is_zoned(sdkp))
+@@ -585,19 +612,19 @@ int sd_zbc_read_zones(struct scsi_disk *
+        * Check zone size: only devices with a constant zone size (except
+        * an eventual last runt zone) that is a power of 2 are supported.
+        */
+-      ret = sd_zbc_check_zone_size(sdkp);
+-      if (ret)
++      zone_blocks = sd_zbc_check_zone_size(sdkp);
++      ret = -EFBIG;
++      if (zone_blocks != (u32)zone_blocks)
++              goto err;
++      ret = zone_blocks;
++      if (ret < 0)
+               goto err;
+       /* The drive satisfies the kernel restrictions: set it up */
+-      ret = sd_zbc_setup(sdkp);
++      ret = sd_zbc_setup(sdkp, zone_blocks);
+       if (ret)
+               goto err;
+-      /* READ16/WRITE16 is mandatory for ZBC disks */
+-      sdkp->device->use_16_for_rw = 1;
+-      sdkp->device->use_10_for_rw = 0;
+-
+       return 0;
+ err:
+@@ -610,6 +637,7 @@ void sd_zbc_remove(struct scsi_disk *sdk
+ {
+       kfree(sdkp->zones_wlock);
+       sdkp->zones_wlock = NULL;
++      sdkp->nr_zones = 0;
+ }
+ void sd_zbc_print_zones(struct scsi_disk *sdkp)
diff --git a/queue-4.14/scsi-sd_zbc-fix-potential-memory-leak.patch b/queue-4.14/scsi-sd_zbc-fix-potential-memory-leak.patch
new file mode 100644 (file)
index 0000000..c6e54dc
--- /dev/null
@@ -0,0 +1,102 @@
+From 4b433924b2755a94f99258c178684a0e05c344de Mon Sep 17 00:00:00 2001
+From: Damien Le Moal <damien.lemoal@wdc.com>
+Date: Fri, 2 Mar 2018 07:19:28 +0900
+Subject: scsi: sd_zbc: Fix potential memory leak
+
+From: Damien Le Moal <damien.lemoal@wdc.com>
+
+commit 4b433924b2755a94f99258c178684a0e05c344de upstream.
+
+Rework sd_zbc_check_zone_size() to avoid a memory leak due to an early
+return if sd_zbc_report_zones() fails.
+
+Reported-by: David.butterfield <david.butterfield@wdc.com>
+Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
+Cc: stable@vger.kernel.org
+Reviewed-by: Bart Van Assche <bart.vanassche@wdc.com>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/scsi/sd_zbc.c |   34 +++++++++++++++-------------------
+ 1 file changed, 15 insertions(+), 19 deletions(-)
+
+--- a/drivers/scsi/sd_zbc.c
++++ b/drivers/scsi/sd_zbc.c
+@@ -425,7 +425,7 @@ static int sd_zbc_check_capacity(struct
+ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
+ {
+-      u64 zone_blocks;
++      u64 zone_blocks = 0;
+       sector_t block = 0;
+       unsigned char *buf;
+       unsigned char *rec;
+@@ -443,10 +443,8 @@ static int sd_zbc_check_zone_size(struct
+       /* Do a report zone to get the same field */
+       ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, 0);
+-      if (ret) {
+-              zone_blocks = 0;
+-              goto out;
+-      }
++      if (ret)
++              goto out_free;
+       same = buf[4] & 0x0f;
+       if (same > 0) {
+@@ -489,7 +487,7 @@ static int sd_zbc_check_zone_size(struct
+                       ret = sd_zbc_report_zones(sdkp, buf,
+                                                 SD_ZBC_BUF_SIZE, block);
+                       if (ret)
+-                              return ret;
++                              goto out_free;
+               }
+       } while (block < sdkp->capacity);
+@@ -497,34 +495,32 @@ static int sd_zbc_check_zone_size(struct
+       zone_blocks = sdkp->zone_blocks;
+ out:
+-      kfree(buf);
+-
+       if (!zone_blocks) {
+               if (sdkp->first_scan)
+                       sd_printk(KERN_NOTICE, sdkp,
+                                 "Devices with non constant zone "
+                                 "size are not supported\n");
+-              return -ENODEV;
+-      }
+-
+-      if (!is_power_of_2(zone_blocks)) {
++              ret = -ENODEV;
++      } else if (!is_power_of_2(zone_blocks)) {
+               if (sdkp->first_scan)
+                       sd_printk(KERN_NOTICE, sdkp,
+                                 "Devices with non power of 2 zone "
+                                 "size are not supported\n");
+-              return -ENODEV;
+-      }
+-
+-      if (logical_to_sectors(sdkp->device, zone_blocks) > UINT_MAX) {
++              ret = -ENODEV;
++      } else if (logical_to_sectors(sdkp->device, zone_blocks) > UINT_MAX) {
+               if (sdkp->first_scan)
+                       sd_printk(KERN_NOTICE, sdkp,
+                                 "Zone size too large\n");
+-              return -ENODEV;
++              ret = -ENODEV;
++      } else {
++              sdkp->zone_blocks = zone_blocks;
++              sdkp->zone_shift = ilog2(zone_blocks);
+       }
+-      sdkp->zone_blocks = zone_blocks;
++out_free:
++      kfree(buf);
+-      return 0;
++      return ret;
+ }
+ static int sd_zbc_setup(struct scsi_disk *sdkp)
diff --git a/queue-4.14/series b/queue-4.14/series
new file mode 100644 (file)
index 0000000..9bea165
--- /dev/null
@@ -0,0 +1,2 @@
+scsi-sd_zbc-fix-potential-memory-leak.patch
+scsi-sd_zbc-avoid-that-resetting-a-zone-fails-sporadically.patch
diff --git a/queue-4.16/series b/queue-4.16/series
new file mode 100644 (file)
index 0000000..e69de29