diff mbox

[V4,07/16] scsi: sd_zbc: Initialize device request queue zoned data

Message ID 20170924070247.25560-8-damien.lemoal@wdc.com (mailing list archive)
State New, archived
Headers show

Commit Message

Damien Le Moal Sept. 24, 2017, 7:02 a.m. UTC
Initialize the seq_zones bitmap and nr_zones field of the disk request
queue on disk revalidate. As the seq_zones bitmap allocation is
identical to the allocation of the zone write lock bitmap, introduce
the helper sd_zbc_alloc_zone_bitmap(). Using this helper, wait for the
disk capacity and number of zones to stabilize on the second
revalidation pass to allocate and initialize the bitmaps.

Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 drivers/scsi/sd_zbc.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 124 insertions(+), 6 deletions(-)

Comments

Christoph Hellwig Sept. 24, 2017, 3:07 p.m. UTC | #1
> +static inline unsigned long *sd_zbc_alloc_zone_bitmap(struct scsi_disk *sdkp)
> +{
> +	struct request_queue *q = sdkp->disk->queue;
> +
> +	return kzalloc_node(BITS_TO_LONGS(sdkp->nr_zones)
> +			    * sizeof(unsigned long),
> +			    GFP_KERNEL, q->node);

This really screams for kcalloc_node and friends that I think Johannes
volunteered to add.

> + * sd_zbc_setup_seq_zones - Initialize the disk request queue zone type bitmap.
> + * @sdkp: The disk of the bitmap
> + *
> + * Allocate a zone bitmap and initialize it by identifying sequential zones.
> + */
> +static int sd_zbc_setup_seq_zones(struct scsi_disk *sdkp)
> +{
> +	struct request_queue *q = sdkp->disk->queue;
> +	unsigned long *seq_zones;
> +	sector_t block = 0;
> +	unsigned char *buf;
> +	unsigned char *rec;
> +	unsigned int buf_len;
> +	unsigned int list_length;
> +	unsigned int n = 0;
> +	u8 type, cond;
> +	int ret = -ENOMEM;
> +
> +	kfree(q->seq_zones);
> +	q->seq_zones = NULL;

We also free the previous version, which isn't documented above.
Which in general begs the question:  What scheme protects access
to q->seq_zones?

And the previous patch should probably grow a comment to document
that q->seq_zones is entirely managed by the driver.

> +		/*
> +		 * Parse reported zone descriptors to find sequiential zones.
> +		 * Since read-only and offline zones cannot be written, do not
> +		 * mark them as sequential in the bitmap.
> +		 */
> +		list_length = get_unaligned_be32(&buf[0]) + 64;
> +		rec = buf + 64;
> +		buf_len = min(list_length, SD_ZBC_BUF_SIZE);
> +		while (rec < buf + buf_len) {
> +			type = rec[0] & 0x0f;
> +			cond = (rec[1] >> 4) & 0xf;
> +			if (type != ZBC_ZONE_TYPE_CONV &&
> +			    cond != ZBC_ZONE_COND_READONLY &&
> +			    cond != ZBC_ZONE_COND_OFFLINE)
> +				set_bit(n, seq_zones);
> +			block = get_unaligned_be64(&rec[8]) +
> +				get_unaligned_be64(&rec[16]);
> +			rec += 64;
> +			n++;
> +		}

Split this out into a helper?
Damien Le Moal Sept. 24, 2017, 4:44 p.m. UTC | #2
On 9/24/17 17:07, Christoph Hellwig wrote:
>> +static inline unsigned long *sd_zbc_alloc_zone_bitmap(struct scsi_disk *sdkp)
>> +{
>> +	struct request_queue *q = sdkp->disk->queue;
>> +
>> +	return kzalloc_node(BITS_TO_LONGS(sdkp->nr_zones)
>> +			    * sizeof(unsigned long),
>> +			    GFP_KERNEL, q->node);
> 
> This really screams for kcalloc_node and friends that I think Johannes
> volunteered to add.

OK. Should I wait for Johannes patches ? That can be easily changed
later though.

>> + * sd_zbc_setup_seq_zones - Initialize the disk request queue zone type bitmap.
>> + * @sdkp: The disk of the bitmap
>> + *
>> + * Allocate a zone bitmap and initialize it by identifying sequential zones.
>> + */
>> +static int sd_zbc_setup_seq_zones(struct scsi_disk *sdkp)
>> +{
>> +	struct request_queue *q = sdkp->disk->queue;
>> +	unsigned long *seq_zones;
>> +	sector_t block = 0;
>> +	unsigned char *buf;
>> +	unsigned char *rec;
>> +	unsigned int buf_len;
>> +	unsigned int list_length;
>> +	unsigned int n = 0;
>> +	u8 type, cond;
>> +	int ret = -ENOMEM;
>> +
>> +	kfree(q->seq_zones);
>> +	q->seq_zones = NULL;
> 
> We also free the previous version, which isn't documented above.
> Which in general begs the question:  What scheme protects access
> to q->seq_zones?

Yes, indeed, the comments do not mention that. I will add that.
As for the protection, there is none necessary I think. The reason is
that the previous version free+alloc can only happen if sd_revalidate is
called, at which point there are no write commands on-going, so no
references to seq_zones. Is this correct/not correct ?

I am not even sure if the reallocation/reinit is even necessary though.
Since sd_revalidate() will at worst result in the disk capacity going to
0 (if the disk is non responsive for instance), accesses beyond
seq_zones size will never happen. And the zone types never change, so it
may be better to drop this reallocation+reinit. Same for the zones_wlock
bitmap. What do you hink ?

> And the previous patch should probably grow a comment to document
> that q->seq_zones is entirely managed by the driver.

Will do.

>> +		/*
>> +		 * Parse reported zone descriptors to find sequiential zones.
>> +		 * Since read-only and offline zones cannot be written, do not
>> +		 * mark them as sequential in the bitmap.
>> +		 */
>> +		list_length = get_unaligned_be32(&buf[0]) + 64;
>> +		rec = buf + 64;
>> +		buf_len = min(list_length, SD_ZBC_BUF_SIZE);
>> +		while (rec < buf + buf_len) {
>> +			type = rec[0] & 0x0f;
>> +			cond = (rec[1] >> 4) & 0xf;
>> +			if (type != ZBC_ZONE_TYPE_CONV &&
>> +			    cond != ZBC_ZONE_COND_READONLY &&
>> +			    cond != ZBC_ZONE_COND_OFFLINE)
>> +				set_bit(n, seq_zones);
>> +			block = get_unaligned_be64(&rec[8]) +
>> +				get_unaligned_be64(&rec[16]);
>> +			rec += 64;
>> +			n++;
>> +		}
> 
> Split this out into a helper?

Yes, that would be a nice cleanup. Will do.

Thanks.
Johannes Thumshirn Sept. 25, 2017, 9:36 a.m. UTC | #3
On Sun, Sep 24, 2017 at 05:07:01PM +0200, Christoph Hellwig wrote:
> > +static inline unsigned long *sd_zbc_alloc_zone_bitmap(struct scsi_disk *sdkp)
> > +{
> > +	struct request_queue *q = sdkp->disk->queue;
> > +
> > +	return kzalloc_node(BITS_TO_LONGS(sdkp->nr_zones)
> > +			    * sizeof(unsigned long),
> > +			    GFP_KERNEL, q->node);
> 
> This really screams for kcalloc_node and friends that I think Johannes
> volunteered to add.

Thanks for the reminder, I'll send out the series today.
diff mbox

Patch

diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c
index 27793b9f54c0..50340c9d265b 100644
--- a/drivers/scsi/sd_zbc.c
+++ b/drivers/scsi/sd_zbc.c
@@ -586,8 +586,110 @@  static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)
 	return 0;
 }
 
+/**
+ * sd_zbc_alloc_zone_bitmap - Allocate a zone bitmap (one bit per zone).
+ * @sdkp: The disk of the bitmap
+ */
+static inline unsigned long *sd_zbc_alloc_zone_bitmap(struct scsi_disk *sdkp)
+{
+	struct request_queue *q = sdkp->disk->queue;
+
+	return kzalloc_node(BITS_TO_LONGS(sdkp->nr_zones)
+			    * sizeof(unsigned long),
+			    GFP_KERNEL, q->node);
+}
+
+/**
+ * sd_zbc_setup_seq_zones - Initialize the disk request queue zone type bitmap.
+ * @sdkp: The disk of the bitmap
+ *
+ * Allocate a zone bitmap and initialize it by identifying sequential zones.
+ */
+static int sd_zbc_setup_seq_zones(struct scsi_disk *sdkp)
+{
+	struct request_queue *q = sdkp->disk->queue;
+	unsigned long *seq_zones;
+	sector_t block = 0;
+	unsigned char *buf;
+	unsigned char *rec;
+	unsigned int buf_len;
+	unsigned int list_length;
+	unsigned int n = 0;
+	u8 type, cond;
+	int ret = -ENOMEM;
+
+	kfree(q->seq_zones);
+	q->seq_zones = NULL;
+
+	seq_zones = sd_zbc_alloc_zone_bitmap(sdkp);
+	if (!seq_zones)
+		return -ENOMEM;
+
+	buf = kmalloc(SD_ZBC_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
+		goto out;
+
+	while (block < sdkp->capacity) {
+
+		ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, block);
+		if (ret)
+			goto out;
+
+		/*
+		 * Parse reported zone descriptors to find sequiential zones.
+		 * Since read-only and offline zones cannot be written, do not
+		 * mark them as sequential in the bitmap.
+		 */
+		list_length = get_unaligned_be32(&buf[0]) + 64;
+		rec = buf + 64;
+		buf_len = min(list_length, SD_ZBC_BUF_SIZE);
+		while (rec < buf + buf_len) {
+			type = rec[0] & 0x0f;
+			cond = (rec[1] >> 4) & 0xf;
+			if (type != ZBC_ZONE_TYPE_CONV &&
+			    cond != ZBC_ZONE_COND_READONLY &&
+			    cond != ZBC_ZONE_COND_OFFLINE)
+				set_bit(n, seq_zones);
+			block = get_unaligned_be64(&rec[8]) +
+				get_unaligned_be64(&rec[16]);
+			rec += 64;
+			n++;
+		}
+
+	}
+
+	if (n != sdkp->nr_zones) {
+		/* Something was wrong */
+		ret = -EIO;
+	}
+
+out:
+	kfree(buf);
+	if (ret) {
+		kfree(seq_zones);
+		return ret;
+	}
+
+	q->seq_zones = seq_zones;
+
+	return 0;
+}
+
+static void sd_zbc_cleanup(struct scsi_disk *sdkp)
+{
+	struct request_queue *q = sdkp->disk->queue;
+
+	kfree(q->seq_zones);
+	q->seq_zones = NULL;
+
+	kfree(sdkp->zones_wlock);
+	sdkp->zones_wlock = NULL;
+}
+
 static int sd_zbc_setup(struct scsi_disk *sdkp)
 {
+	struct request_queue *q = sdkp->disk->queue;
+	int ret;
 
 	/* READ16/WRITE16 is mandatory for ZBC disks */
 	sdkp->device->use_16_for_rw = 1;
@@ -599,14 +701,30 @@  static int sd_zbc_setup(struct scsi_disk *sdkp)
 	sdkp->nr_zones =
 		round_up(sdkp->capacity, sdkp->zone_blocks) >> sdkp->zone_shift;
 
-	if (!sdkp->zones_wlock) {
-		sdkp->zones_wlock = kcalloc(BITS_TO_LONGS(sdkp->nr_zones),
-					    sizeof(unsigned long),
-					    GFP_KERNEL);
+	/*
+	 * Wait for the disk capacity to stabilize before
+	 * initializing zone related information.
+	 */
+	if (sdkp->first_scan)
+		return 0;
+
+	if (!sdkp->zones_wlock || q->nr_zones != sdkp->nr_zones) {
+		kfree(sdkp->zones_wlock);
+		sdkp->zones_wlock = sd_zbc_alloc_zone_bitmap(sdkp);
 		if (!sdkp->zones_wlock)
 			return -ENOMEM;
 	}
 
+	if (!q->seq_zones || q->nr_zones != sdkp->nr_zones) {
+		ret = sd_zbc_setup_seq_zones(sdkp);
+		if (ret) {
+			sd_zbc_cleanup(sdkp);
+			return ret;
+		}
+	}
+
+	q->nr_zones = sdkp->nr_zones;
+
 	return 0;
 }
 
@@ -661,14 +779,14 @@  int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
 
 err:
 	sdkp->capacity = 0;
+	sd_zbc_cleanup(sdkp);
 
 	return ret;
 }
 
 void sd_zbc_remove(struct scsi_disk *sdkp)
 {
-	kfree(sdkp->zones_wlock);
-	sdkp->zones_wlock = NULL;
+	sd_zbc_cleanup(sdkp);
 }
 
 void sd_zbc_print_zones(struct scsi_disk *sdkp)