sd: Export preferred I/O sizes

Make storage devices that support the Block Limits VPD export the
optimal transfer length granularity and the optimal transfer length to
the block layer.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

---

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1341,6 +1341,10 @@ got_data:
 			  hard_sector, (unsigned long long)mb);
 	}
 
+	disk_topology_set(sdkp->disk, 0, IOT_REGION_START, 0);
+	disk_topology_set(sdkp->disk, 0, IOT_REGION_LENGTH,
+			  sdkp->capacity * sector_size);
+
 	/* Rescale capacity to 512-byte units */
 	if (sector_size == 4096)
 		sdkp->capacity <<= 3;
@@ -1535,6 +1539,50 @@ defaults:
 }
 
 /**
+ * sd_block_limits - Query disk device for preferred I/O sizes.
+ * @disk: disk to query
+ * @buffer: temporary buffer to store inquiry response in
+ */
+static void sd_block_limits(struct scsi_disk *sdkp, unsigned char *buffer)
+{
+	/* Block Limits VPD */
+	if (scsi_get_vpd_page(sdkp->device, buffer, 0xb0, 16) < 0)
+		return;
+
+	disk_topology_set(sdkp->disk, 0, IOT_PHYS_OFF, 0);
+	disk_topology_set(sdkp->disk, 0, IOT_OPT_BLOCK_SIZE,
+			  be16_to_cpup((__be16 *)&buffer[6]) 
+			  * sdkp->device->sector_size);
+	disk_topology_set(sdkp->disk, 0, IOT_MAX_IO_LENGTH,
+			  be32_to_cpup((__be32 *)&buffer[8])
+			  * sdkp->device->sector_size);
+	disk_topology_set(sdkp->disk, 0, IOT_OPT_IO_LENTGH,
+			  be32_to_cpup((__be32 *)&buffer[12])
+			  * sdkp->device->sector_size);
+}
+
+/**
+ * sd_block_characteristics - Query block dev. characteristics
+ * @disk: disk to query
+ * @buffer: temporary buffer to store inquiry response in
+ */
+static void sd_block_characteristics(struct scsi_disk *sdkp, unsigned char *buffer)
+{
+	unsigned int rotation;
+
+	/* Block Device Characteristics VPD */
+	if (scsi_get_vpd_page(sdkp->device, buffer, 0xb1, 64) < 0)
+		return;
+
+	rotation = be16_to_cpup((__be16 *)&buffer[4]);
+
+	if (rotation == 1)
+		disk_topology_set(sdkp->disk, 0, IOT_DEV_TYPE, IOT_TYPE_SSD);
+	else if (rotation > 0x400)
+		disk_topology_set(sdkp->disk, 0, IOT_DEV_TYPE, IOT_TYPE_DISK);
+}
+
+/**
  *	sd_revalidate_disk - called the first time a new disk is seen,
  *	performs disk spin up, read_capacity, etc.
  *	@disk: struct gendisk we care about
@@ -1579,6 +1627,8 @@ static int sd_revalidate_disk(struct gen
 	 */
 	if (sdkp->media_present) {
 		sd_read_capacity(sdkp, buffer);
+		sd_block_limits(sdkp, buffer);
+		sd_block_characteristics(sdkp, buffer);
 		sd_read_write_protect_flag(sdkp, buffer);
 		sd_read_cache_type(sdkp, buffer);
 	}
@@ -1703,8 +1753,6 @@ static int sd_probe(struct device *dev)
 	gd->private_data = &sdkp->driver;
 	gd->queue = sdkp->device->request_queue;
 
-	sd_revalidate_disk(gd);
-
 	blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);
 
 	gd->driverfs_dev = &sdp->sdev_gendev;
@@ -1714,6 +1762,12 @@ static int sd_probe(struct device *dev)
 
 	dev_set_drvdata(dev, sdkp);
 	add_disk(gd);
+
+	disk_topology_register(gd);
+	disk_topology_add_region(gd, 0, 0);
+	disk_topology_add_region(gd, 10, 20);
+
+	sd_revalidate_disk(gd);
 
 	sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
 		  sdp->removable ? "removable " : "");
@@ -1744,6 +1798,7 @@ static int sd_remove(struct device *dev)
 	struct scsi_disk *sdkp = dev_get_drvdata(dev);
 
 	device_del(&sdkp->dev);
+	disk_topology_unregister(sdkp->disk);
 	del_gendisk(sdkp->disk);
 	sd_shutdown(dev);
 
