[SCSI] sd: Update thin provisioning support
authorMartin K. Petersen <martin.petersen@oracle.com>
Fri, 10 Sep 2010 05:22:07 +0000 (01:22 -0400)
committerJames Bottomley <James.Bottomley@suse.de>
Fri, 17 Sep 2010 17:07:55 +0000 (13:07 -0400)
Add support for the Thin Provisioning VPD page and use the TPU and TPWS
bits to switch between UNMAP and WRITE SAME(16) for discards.  If no TP
VPD page is present we fall back to old scheme where the max descriptor
count combined with the max lba count are used trigger UNMAP.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/sd.c
drivers/scsi/sd.h

index 8c9b275f71a76f019e872407099d6415e5df4b65..0c4f89cfb7dcdd8d05ec1c49fc6b5d69fac1ae86 100644 (file)
@@ -2039,14 +2039,24 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
                lba_count = get_unaligned_be32(&buffer[20]);
                desc_count = get_unaligned_be32(&buffer[24]);
 
-               if (lba_count) {
-                       q->limits.max_discard_sectors =
-                               lba_count * sector_sz >> 9;
-
-                       if (desc_count)
+               if (lba_count && desc_count) {
+                       if (sdkp->tpvpd && !sdkp->tpu)
+                               sdkp->unmap = 0;
+                       else
                                sdkp->unmap = 1;
                }
 
+               if (sdkp->tpvpd && !sdkp->tpu && !sdkp->tpws) {
+                       sd_printk(KERN_ERR, sdkp, "Thin provisioning is " \
+                                 "enabled but neither TPU, nor TPWS are " \
+                                 "set. Disabling discard!\n");
+                       goto out;
+               }
+
+               if (lba_count)
+                       q->limits.max_discard_sectors =
+                               lba_count * sector_sz >> 9;
+
                granularity = get_unaligned_be32(&buffer[28]);
 
                if (granularity)
@@ -2087,6 +2097,31 @@ static void sd_read_block_characteristics(struct scsi_disk *sdkp)
        kfree(buffer);
 }
 
+/**
+ * sd_read_thin_provisioning - Query thin provisioning VPD page
+ * @disk: disk to query
+ */
+static void sd_read_thin_provisioning(struct scsi_disk *sdkp)
+{
+       unsigned char *buffer;
+       const int vpd_len = 8;
+
+       if (sdkp->thin_provisioning == 0)
+               return;
+
+       buffer = kmalloc(vpd_len, GFP_KERNEL);
+
+       if (!buffer || scsi_get_vpd_page(sdkp->device, 0xb2, buffer, vpd_len))
+               goto out;
+
+       sdkp->tpvpd = 1;
+       sdkp->tpu   = (buffer[5] >> 7) & 1;     /* UNMAP */
+       sdkp->tpws  = (buffer[5] >> 6) & 1;     /* WRITE SAME(16) with UNMAP */
+
+ out:
+       kfree(buffer);
+}
+
 static int sd_try_extended_inquiry(struct scsi_device *sdp)
 {
        /*
@@ -2138,6 +2173,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
                sd_read_capacity(sdkp, buffer);
 
                if (sd_try_extended_inquiry(sdp)) {
+                       sd_read_thin_provisioning(sdkp);
                        sd_read_block_limits(sdkp);
                        sd_read_block_characteristics(sdkp);
                }
index 315ce9d96b1fd671c086ce371bda152aff8d2ce4..a40730ee465ce1021f9799afce67ec15afa41023 100644 (file)
@@ -63,6 +63,9 @@ struct scsi_disk {
        unsigned        first_scan : 1;
        unsigned        thin_provisioning : 1;
        unsigned        unmap : 1;
+       unsigned        tpws : 1;
+       unsigned        tpu : 1;
+       unsigned        tpvpd : 1;
 };
 #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)