diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 8c9b275f71a7..0c4f89cfb7dc 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -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); } diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 315ce9d96b1f..a40730ee465c 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -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)