Skip to content

Commit

Permalink
scsi: sd_zbc: Improve zone revalidation
Browse files Browse the repository at this point in the history
Currently, for zoned disks, since blk_revalidate_disk_zones() requires the
disk capacity to be set already to operate correctly, zones revalidation
can only be done on the second revalidate scan once the gendisk capacity is
set at the end of the first scan. As a result, if zone revalidation fails,
there is no second chance to recover from the failure and the disk capacity
is changed to 0, with the disk left unusable.

This can be improved by shuffling around code, specifically, by moving the
call to sd_zbc_revalidate_zones() from sd_zbc_read_zones() to the end of
sd_revalidate_disk(), after set_capacity_revalidate_and_notify() is called
to set the gendisk capacity. With this change, if sd_zbc_revalidate_zones()
fails on the first scan, the second scan will call it again to recover, if
possible.

Using the new struct scsi_disk fields rev_nr_zones and rev_zone_blocks,
sd_zbc_revalidate_zones() does actual work only if it detects a change with
the disk zone configuration. This means that for a successful zones
revalidation on the first scan, the second scan will not cause another
heavy full check.

While at it, remove the unecesary "extern" declaration of
sd_zbc_read_zones().

Link: https://lore.kernel.org/r/20200731054928.668547-1-damien.lemoal@wdc.com
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
Damien Le Moal authored and Martin K. Petersen committed Aug 5, 2020
1 parent ec007ef commit a3d8a25
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 54 deletions.
10 changes: 8 additions & 2 deletions drivers/scsi/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -2578,8 +2578,6 @@ sd_print_capacity(struct scsi_disk *sdkp,
sd_printk(KERN_NOTICE, sdkp,
"%u-byte physical blocks\n",
sdkp->physical_block_size);

sd_zbc_print_zones(sdkp);
}

/* called with buffer of length 512 */
Expand Down Expand Up @@ -3220,6 +3218,14 @@ static int sd_revalidate_disk(struct gendisk *disk)
sd_config_write_same(sdkp);
kfree(buffer);

/*
* For a zoned drive, revalidating the zones can be done only once
* the gendisk capacity is set. So if this fails, set back the gendisk
* capacity to 0.
*/
if (sd_zbc_revalidate_zones(sdkp))
set_capacity_revalidate_and_notify(disk, 0, false);

out:
return 0;
}
Expand Down
11 changes: 8 additions & 3 deletions drivers/scsi/sd.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ struct scsi_disk {
struct opal_dev *opal_dev;
#ifdef CONFIG_BLK_DEV_ZONED
u32 nr_zones;
u32 rev_nr_zones;
u32 zone_blocks;
u32 rev_zone_blocks;
u32 zones_optimal_open;
u32 zones_optimal_nonseq;
u32 zones_max_open;
Expand Down Expand Up @@ -215,8 +217,8 @@ static inline int sd_is_zoned(struct scsi_disk *sdkp)

int sd_zbc_init_disk(struct scsi_disk *sdkp);
void sd_zbc_release_disk(struct scsi_disk *sdkp);
extern int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buffer);
extern void sd_zbc_print_zones(struct scsi_disk *sdkp);
int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buffer);
int sd_zbc_revalidate_zones(struct scsi_disk *sdkp);
blk_status_t sd_zbc_setup_zone_mgmt_cmnd(struct scsi_cmnd *cmd,
unsigned char op, bool all);
unsigned int sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes,
Expand All @@ -242,7 +244,10 @@ static inline int sd_zbc_read_zones(struct scsi_disk *sdkp,
return 0;
}

static inline void sd_zbc_print_zones(struct scsi_disk *sdkp) {}
static inline int sd_zbc_revalidate_zones(struct scsi_disk *sdkp)
{
return 0;
}

static inline blk_status_t sd_zbc_setup_zone_mgmt_cmnd(struct scsi_cmnd *cmd,
unsigned char op,
Expand Down
93 changes: 44 additions & 49 deletions drivers/scsi/sd_zbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -633,43 +633,55 @@ static int sd_zbc_check_capacity(struct scsi_disk *sdkp, unsigned char *buf,
return 0;
}

static void sd_zbc_print_zones(struct scsi_disk *sdkp)
{
if (!sd_is_zoned(sdkp) || !sdkp->capacity)
return;

if (sdkp->capacity & (sdkp->zone_blocks - 1))
sd_printk(KERN_NOTICE, sdkp,
"%u zones of %u logical blocks + 1 runt zone\n",
sdkp->nr_zones - 1,
sdkp->zone_blocks);
else
sd_printk(KERN_NOTICE, sdkp,
"%u zones of %u logical blocks\n",
sdkp->nr_zones,
sdkp->zone_blocks);
}

static void sd_zbc_revalidate_zones_cb(struct gendisk *disk)
{
struct scsi_disk *sdkp = scsi_disk(disk);

swap(sdkp->zones_wp_offset, sdkp->rev_wp_offset);
}

static int sd_zbc_revalidate_zones(struct scsi_disk *sdkp,
u32 zone_blocks,
unsigned int nr_zones)
int sd_zbc_revalidate_zones(struct scsi_disk *sdkp)
{
struct gendisk *disk = sdkp->disk;
struct request_queue *q = disk->queue;
u32 zone_blocks = sdkp->rev_zone_blocks;
unsigned int nr_zones = sdkp->rev_nr_zones;
u32 max_append;
int ret = 0;

if (!sd_is_zoned(sdkp))
return 0;

/*
* Make sure revalidate zones are serialized to ensure exclusive
* updates of the scsi disk data.
*/
mutex_lock(&sdkp->rev_mutex);

/*
* Revalidate the disk zones to update the device request queue zone
* bitmaps and the zone write pointer offset array. Do this only once
* the device capacity is set on the second revalidate execution for
* disk scan or if something changed when executing a normal revalidate.
*/
if (sdkp->first_scan) {
sdkp->zone_blocks = zone_blocks;
sdkp->nr_zones = nr_zones;
goto unlock;
}

if (sdkp->zone_blocks == zone_blocks &&
sdkp->nr_zones == nr_zones &&
disk->queue->nr_zones == nr_zones)
goto unlock;

sdkp->zone_blocks = zone_blocks;
sdkp->nr_zones = nr_zones;
sdkp->rev_wp_offset = kvcalloc(nr_zones, sizeof(u32), GFP_NOIO);
if (!sdkp->rev_wp_offset) {
ret = -ENOMEM;
Expand All @@ -681,6 +693,21 @@ static int sd_zbc_revalidate_zones(struct scsi_disk *sdkp,
kvfree(sdkp->rev_wp_offset);
sdkp->rev_wp_offset = NULL;

if (ret) {
sdkp->zone_blocks = 0;
sdkp->nr_zones = 0;
sdkp->capacity = 0;
goto unlock;
}

max_append = min_t(u32, logical_to_sectors(sdkp->device, zone_blocks),
q->limits.max_segments << (PAGE_SHIFT - 9));
max_append = min_t(u32, max_append, queue_max_hw_sectors(q));

blk_queue_max_zone_append_sectors(q, max_append);

sd_zbc_print_zones(sdkp);

unlock:
mutex_unlock(&sdkp->rev_mutex);

Expand All @@ -693,7 +720,6 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
struct request_queue *q = disk->queue;
unsigned int nr_zones;
u32 zone_blocks = 0;
u32 max_append;
int ret;

if (!sd_is_zoned(sdkp))
Expand Down Expand Up @@ -722,22 +748,8 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
sdkp->device->use_16_for_rw = 1;
sdkp->device->use_10_for_rw = 0;

ret = sd_zbc_revalidate_zones(sdkp, zone_blocks, nr_zones);
if (ret)
goto err;

/*
* On the first scan 'chunk_sectors' isn't setup yet, so calling
* blk_queue_max_zone_append_sectors() will result in a WARN(). Defer
* this setting to the second scan.
*/
if (sdkp->first_scan)
return 0;

max_append = min_t(u32, logical_to_sectors(sdkp->device, zone_blocks),
q->limits.max_segments << (PAGE_SHIFT - 9));

blk_queue_max_zone_append_sectors(q, max_append);
sdkp->rev_nr_zones = nr_zones;
sdkp->rev_zone_blocks = zone_blocks;

return 0;

Expand All @@ -747,23 +759,6 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
return ret;
}

void sd_zbc_print_zones(struct scsi_disk *sdkp)
{
if (!sd_is_zoned(sdkp) || !sdkp->capacity)
return;

if (sdkp->capacity & (sdkp->zone_blocks - 1))
sd_printk(KERN_NOTICE, sdkp,
"%u zones of %u logical blocks + 1 runt zone\n",
sdkp->nr_zones - 1,
sdkp->zone_blocks);
else
sd_printk(KERN_NOTICE, sdkp,
"%u zones of %u logical blocks\n",
sdkp->nr_zones,
sdkp->zone_blocks);
}

int sd_zbc_init_disk(struct scsi_disk *sdkp)
{
if (!sd_is_zoned(sdkp))
Expand Down

0 comments on commit a3d8a25

Please sign in to comment.