summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r--drivers/scsi/sd.c116
1 files changed, 84 insertions, 32 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 2714bec..57d1e3e 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -477,7 +477,7 @@ static int scsi_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq)
static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq)
{
- rq->timeout = SD_TIMEOUT;
+ rq->timeout = SD_FLUSH_TIMEOUT;
rq->retries = SD_MAX_RETRIES;
rq->cmd[0] = SYNCHRONIZE_CACHE;
rq->cmd_len = 10;
@@ -870,7 +870,7 @@ static int sd_release(struct gendisk *disk, fmode_t mode)
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
- if (atomic_dec_return(&sdkp->openers) && sdev->removable) {
+ if (atomic_dec_return(&sdkp->openers) == 0 && sdev->removable) {
if (scsi_block_when_processing_errors(sdev))
scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
}
@@ -1072,7 +1072,7 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
* flush everything.
*/
res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+ SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL);
if (res == 0)
break;
}
@@ -1498,6 +1498,9 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
unsigned long long lba;
unsigned sector_size;
+ if (sdp->no_read_capacity_16)
+ return -EINVAL;
+
do {
memset(cmd, 0, 16);
cmd[0] = SERVICE_ACTION_IN;
@@ -1554,7 +1557,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
}
/* Logical blocks per physical block exponent */
- sdkp->hw_sector_size = (1 << (buffer[13] & 0xf)) * sector_size;
+ sdkp->physical_block_size = (1 << (buffer[13] & 0xf)) * sector_size;
/* Lowest aligned logical block */
alignment = ((buffer[14] & 0x3f) << 8 | buffer[15]) * sector_size;
@@ -1567,7 +1570,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
struct request_queue *q = sdp->request_queue;
sdkp->thin_provisioning = 1;
- q->limits.discard_granularity = sdkp->hw_sector_size;
+ q->limits.discard_granularity = sdkp->physical_block_size;
q->limits.max_discard_sectors = 0xffffffff;
if (buffer[14] & 0x40) /* TPRZ */
@@ -1626,6 +1629,15 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp,
sector_size = get_unaligned_be32(&buffer[4]);
lba = get_unaligned_be32(&buffer[0]);
+ if (sdp->no_read_capacity_16 && (lba == 0xffffffff)) {
+ /* Some buggy (usb cardreader) devices return an lba of
+ 0xffffffff when the want to report a size of 0 (with
+ which they really mean no media is present) */
+ sdkp->capacity = 0;
+ sdkp->physical_block_size = sector_size;
+ return sector_size;
+ }
+
if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
"kernel compiled with support for large block "
@@ -1635,7 +1647,7 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp,
}
sdkp->capacity = lba + 1;
- sdkp->hw_sector_size = sector_size;
+ sdkp->physical_block_size = sector_size;
return sector_size;
}
@@ -1756,10 +1768,10 @@ got_data:
(unsigned long long)sdkp->capacity,
sector_size, cap_str_10, cap_str_2);
- if (sdkp->hw_sector_size != sector_size)
+ if (sdkp->physical_block_size != sector_size)
sd_printk(KERN_NOTICE, sdkp,
"%u-byte physical blocks\n",
- sdkp->hw_sector_size);
+ sdkp->physical_block_size);
}
}
@@ -1773,7 +1785,8 @@ got_data:
else if (sector_size == 256)
sdkp->capacity >>= 1;
- blk_queue_physical_block_size(sdp->request_queue, sdkp->hw_sector_size);
+ blk_queue_physical_block_size(sdp->request_queue,
+ sdkp->physical_block_size);
sdkp->device->sector_size = sector_size;
}
@@ -2039,14 +2052,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 +2110,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)
{
/*
@@ -2109,7 +2157,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
struct scsi_disk *sdkp = scsi_disk(disk);
struct scsi_device *sdp = sdkp->device;
unsigned char *buffer;
- unsigned ordered;
+ unsigned flush = 0;
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
"sd_revalidate_disk\n"));
@@ -2138,6 +2186,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);
}
@@ -2151,17 +2200,15 @@ static int sd_revalidate_disk(struct gendisk *disk)
/*
* We now have all cache related info, determine how we deal
- * with ordered requests. Note that as the current SCSI
- * dispatch function can alter request order, we cannot use
- * QUEUE_ORDERED_TAG_* even when ordered tag is supported.
+ * with flush requests.
*/
- if (sdkp->WCE)
- ordered = sdkp->DPOFUA
- ? QUEUE_ORDERED_DRAIN_FUA : QUEUE_ORDERED_DRAIN_FLUSH;
- else
- ordered = QUEUE_ORDERED_DRAIN;
+ if (sdkp->WCE) {
+ flush |= REQ_FLUSH;
+ if (sdkp->DPOFUA)
+ flush |= REQ_FUA;
+ }
- blk_queue_ordered(sdkp->disk->queue, ordered);
+ blk_queue_flush(sdkp->disk->queue, flush);
set_capacity(disk, sdkp->capacity);
kfree(buffer);
@@ -2252,11 +2299,10 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
index = sdkp->index;
dev = &sdp->sdev_gendev;
- if (index < SD_MAX_DISKS) {
- gd->major = sd_major((index & 0xf0) >> 4);
- gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
- gd->minors = SD_MINORS;
- }
+ gd->major = sd_major((index & 0xf0) >> 4);
+ gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
+ gd->minors = SD_MINORS;
+
gd->fops = &sd_fops;
gd->private_data = &sdkp->driver;
gd->queue = sdkp->device->request_queue;
@@ -2346,6 +2392,12 @@ static int sd_probe(struct device *dev)
if (error)
goto out_put;
+ if (index >= SD_MAX_DISKS) {
+ error = -ENODEV;
+ sdev_printk(KERN_WARNING, sdp, "SCSI disk (sd) name space exhausted.\n");
+ goto out_free_index;
+ }
+
error = sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN);
if (error)
goto out_free_index;
@@ -2625,15 +2677,15 @@ module_exit(exit_sd);
static void sd_print_sense_hdr(struct scsi_disk *sdkp,
struct scsi_sense_hdr *sshdr)
{
- sd_printk(KERN_INFO, sdkp, "");
+ sd_printk(KERN_INFO, sdkp, " ");
scsi_show_sense_hdr(sshdr);
- sd_printk(KERN_INFO, sdkp, "");
+ sd_printk(KERN_INFO, sdkp, " ");
scsi_show_extd_sense(sshdr->asc, sshdr->ascq);
}
static void sd_print_result(struct scsi_disk *sdkp, int result)
{
- sd_printk(KERN_INFO, sdkp, "");
+ sd_printk(KERN_INFO, sdkp, " ");
scsi_show_result(result);
}
OpenPOWER on IntegriCloud