* [PATCH 0/2] 4.14 long term stable ZBC fixes
@ 2018-05-31 10:31 Damien Le Moal
2018-05-31 10:31 ` [PATCH 1/2] scsi: sd_zbc: Fix potential memory leak Damien Le Moal
2018-05-31 10:31 ` [PATCH 2/2] scsi: sd_zbc: Avoid that resetting a zone fails sporadically Damien Le Moal
0 siblings, 2 replies; 4+ messages in thread
From: Damien Le Moal @ 2018-05-31 10:31 UTC (permalink / raw)
To: linux-scsi, Martin K . Petersen
Cc: stable, Greg Kroah-Hartman, Bart Van Assche, Hannes Reinecke
Patch 0aa3fdb8b3a6 ("scsi: sd_zbc: Fix potential memory leak") was added in
4.16 and 4.15 stable but did not make it to long term stable 4.14 (as far as I
can tell).
Patch ccce20fc7968 ("scsi: sd_zbc: Avoid that resetting a zone fails
sporadically") is included in 4.16 but does not apply to 4.15 stable nor to
4.14 long term stable and requires extensive modifications.
This small series provides a backport of both patches against 4.14. Please
consider these patches for inclusion in this long term stable kernel.
Bart Van Assche (1):
scsi: sd_zbc: Avoid that resetting a zone fails sporadically
Damien Le Moal (1):
scsi: sd_zbc: Fix potential memory leak
drivers/scsi/sd_zbc.c | 128 +++++++++++++++++++++++++-----------------
1 file changed, 76 insertions(+), 52 deletions(-)
--
2.17.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] scsi: sd_zbc: Fix potential memory leak
2018-05-31 10:31 [PATCH 0/2] 4.14 long term stable ZBC fixes Damien Le Moal
@ 2018-05-31 10:31 ` Damien Le Moal
2018-06-02 13:22 ` Greg Kroah-Hartman
2018-05-31 10:31 ` [PATCH 2/2] scsi: sd_zbc: Avoid that resetting a zone fails sporadically Damien Le Moal
1 sibling, 1 reply; 4+ messages in thread
From: Damien Le Moal @ 2018-05-31 10:31 UTC (permalink / raw)
To: linux-scsi, Martin K . Petersen
Cc: stable, Greg Kroah-Hartman, Bart Van Assche, Hannes Reinecke
[Backport of upstream commit 0aa3fdb8b3a6df3c2e3b61dbfe079db9d30e03cd]
Rework sd_zbc_check_zone_size() to avoid a memory leak due to an early
return if sd_zbc_report_zones() fails.
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Cc: stable@vger.kernel.org # 4.14
---
drivers/scsi/sd_zbc.c | 34 +++++++++++++++-------------------
1 file changed, 15 insertions(+), 19 deletions(-)
diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c
index 2eb61d54bbb4..bc3cb81a9c7d 100644
--- a/drivers/scsi/sd_zbc.c
+++ b/drivers/scsi/sd_zbc.c
@@ -425,7 +425,7 @@ static int sd_zbc_check_capacity(struct scsi_disk *sdkp,
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 scsi_disk *sdkp)
/* 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 scsi_disk *sdkp)
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 scsi_disk *sdkp)
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)
--
2.17.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] scsi: sd_zbc: Avoid that resetting a zone fails sporadically
2018-05-31 10:31 [PATCH 0/2] 4.14 long term stable ZBC fixes Damien Le Moal
2018-05-31 10:31 ` [PATCH 1/2] scsi: sd_zbc: Fix potential memory leak Damien Le Moal
@ 2018-05-31 10:31 ` Damien Le Moal
1 sibling, 0 replies; 4+ messages in thread
From: Damien Le Moal @ 2018-05-31 10:31 UTC (permalink / raw)
To: linux-scsi, Martin K . Petersen
Cc: stable, Greg Kroah-Hartman, Bart Van Assche, Hannes Reinecke,
Bart Van Assche
From: Bart Van Assche <bart.vanassche@wdc.com>
[Backport of upstream commit ccce20fc7968d546fb1e8e147bf5cdc8afc4278a]
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() sets sdkp->nr_zones to zero before restoring it to
the actual value, even if no drive characteristics have changed.
Avoid that this can happen by modifying making the following changes:
- Protect the code that updates zone information with blk_mq_freeze()
and blk_mq_unfreeze().
- Modify sd_zbc_setup() such that these functions do not modify
struct scsi_disk before all zone information has been obtained.
- Reallocate the zone write lock bitmap if the number of zones changed.
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>
[Damien]
* Backport for 4.14-stable
* Updated this commit message
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Cc: stable@vger.kernel.org # 4.14
---
drivers/scsi/sd_zbc.c | 98 +++++++++++++++++++++++++++----------------
1 file changed, 63 insertions(+), 35 deletions(-)
diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c
index bc3cb81a9c7d..ea9e1e0ed5b8 100644
--- a/drivers/scsi/sd_zbc.c
+++ b/drivers/scsi/sd_zbc.c
@@ -423,7 +423,16 @@ static int sd_zbc_check_capacity(struct scsi_disk *sdkp,
#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 scsi_disk *sdkp)
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 scsi_disk *sdkp)
/* 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 scsi_disk *sdkp)
} while (block < sdkp->capacity);
- zone_blocks = sdkp->zone_blocks;
-
out:
if (!zone_blocks) {
if (sdkp->first_scan)
@@ -513,8 +519,7 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
"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 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
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 *sdkp)
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 *sdkp,
* 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 *sdkp)
{
kfree(sdkp->zones_wlock);
sdkp->zones_wlock = NULL;
+ sdkp->nr_zones = 0;
}
void sd_zbc_print_zones(struct scsi_disk *sdkp)
--
2.17.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 1/2] scsi: sd_zbc: Fix potential memory leak
2018-05-31 10:31 ` [PATCH 1/2] scsi: sd_zbc: Fix potential memory leak Damien Le Moal
@ 2018-06-02 13:22 ` Greg Kroah-Hartman
0 siblings, 0 replies; 4+ messages in thread
From: Greg Kroah-Hartman @ 2018-06-02 13:22 UTC (permalink / raw)
To: Damien Le Moal
Cc: linux-scsi, Martin K . Petersen, stable, Bart Van Assche,
Hannes Reinecke
On Thu, May 31, 2018 at 07:31:23PM +0900, Damien Le Moal wrote:
> [Backport of upstream commit 0aa3fdb8b3a6df3c2e3b61dbfe079db9d30e03cd]
That is not what this commit id is :(
Please fix up and resend the series.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2018-06-02 13:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-05-31 10:31 [PATCH 0/2] 4.14 long term stable ZBC fixes Damien Le Moal
2018-05-31 10:31 ` [PATCH 1/2] scsi: sd_zbc: Fix potential memory leak Damien Le Moal
2018-06-02 13:22 ` Greg Kroah-Hartman
2018-05-31 10:31 ` [PATCH 2/2] scsi: sd_zbc: Avoid that resetting a zone fails sporadically Damien Le Moal
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.