summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sd.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-03-28 21:30:43 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2009-03-28 21:30:43 +0100
commitd54b3538b0bfb31351d02d1669d4a978d2abfc5f (patch)
tree5ce539ecba525b30bbfb1c46c55487099264947e /drivers/scsi/sd.c
parentMerge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6 (diff)
parent[SCSI] scsi_dh_rdac: Retry for NOT_READY check condition (diff)
downloadlinux-d54b3538b0bfb31351d02d1669d4a978d2abfc5f.tar.xz
linux-d54b3538b0bfb31351d02d1669d4a978d2abfc5f.zip
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (119 commits) [SCSI] scsi_dh_rdac: Retry for NOT_READY check condition [SCSI] mpt2sas: make global symbols unique [SCSI] sd: Make revalidate less chatty [SCSI] sd: Try READ CAPACITY 16 first for SBC-2 devices [SCSI] sd: Refactor sd_read_capacity() [SCSI] mpt2sas v00.100.11.15 [SCSI] mpt2sas: add MPT2SAS_MINOR(221) to miscdevice.h [SCSI] ch: Add scsi type modalias [SCSI] 3w-9xxx: add power management support [SCSI] bsg: add linux/types.h include to bsg.h [SCSI] cxgb3i: fix function descriptions [SCSI] libiscsi: fix possbile null ptr session command cleanup [SCSI] iscsi class: remove host no argument from session creation callout [SCSI] libiscsi: pass session failure a session struct [SCSI] iscsi lib: remove qdepth param from iscsi host allocation [SCSI] iscsi lib: have lib create work queue for transmitting IO [SCSI] iscsi class: fix lock dep warning on logout [SCSI] libiscsi: don't cap queue depth in iscsi modules [SCSI] iscsi_tcp: replace scsi_debug/tcp_debug logging with iscsi conn logging [SCSI] libiscsi_tcp: replace tcp_debug/scsi_debug logging with session/conn logging ...
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r--drivers/scsi/sd.c349
1 files changed, 228 insertions, 121 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 4970ae4a62d6..aeab5d9dff27 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1273,42 +1273,126 @@ disable:
sdkp->capacity = 0;
}
-/*
- * read disk capacity
- */
-static void
-sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)
+static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp,
+ struct scsi_sense_hdr *sshdr, int sense_valid,
+ int the_result)
+{
+ sd_print_result(sdkp, the_result);
+ if (driver_byte(the_result) & DRIVER_SENSE)
+ sd_print_sense_hdr(sdkp, sshdr);
+ else
+ sd_printk(KERN_NOTICE, sdkp, "Sense not available.\n");
+
+ /*
+ * Set dirty bit for removable devices if not ready -
+ * sometimes drives will not report this properly.
+ */
+ if (sdp->removable &&
+ sense_valid && sshdr->sense_key == NOT_READY)
+ sdp->changed = 1;
+
+ /*
+ * We used to set media_present to 0 here to indicate no media
+ * in the drive, but some drives fail read capacity even with
+ * media present, so we can't do that.
+ */
+ sdkp->capacity = 0; /* unknown mapped to zero - as usual */
+}
+
+#define RC16_LEN 32
+#if RC16_LEN > SD_BUF_SIZE
+#error RC16_LEN must not be more than SD_BUF_SIZE
+#endif
+
+static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
+ unsigned char *buffer)
{
unsigned char cmd[16];
- int the_result, retries;
- int sector_size = 0;
- /* Force READ CAPACITY(16) when PROTECT=1 */
- int longrc = scsi_device_protection(sdkp->device) ? 1 : 0;
struct scsi_sense_hdr sshdr;
int sense_valid = 0;
- struct scsi_device *sdp = sdkp->device;
+ int the_result;
+ int retries = 3;
+ unsigned long long lba;
+ unsigned sector_size;
-repeat:
- retries = 3;
do {
- if (longrc) {
- memset((void *) cmd, 0, 16);
- cmd[0] = SERVICE_ACTION_IN;
- cmd[1] = SAI_READ_CAPACITY_16;
- cmd[13] = 13;
- memset((void *) buffer, 0, 13);
- } else {
- cmd[0] = READ_CAPACITY;
- memset((void *) &cmd[1], 0, 9);
- memset((void *) buffer, 0, 8);
+ memset(cmd, 0, 16);
+ cmd[0] = SERVICE_ACTION_IN;
+ cmd[1] = SAI_READ_CAPACITY_16;
+ cmd[13] = RC16_LEN;
+ memset(buffer, 0, RC16_LEN);
+
+ the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
+ buffer, RC16_LEN, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+
+ if (media_not_present(sdkp, &sshdr))
+ return -ENODEV;
+
+ if (the_result) {
+ sense_valid = scsi_sense_valid(&sshdr);
+ if (sense_valid &&
+ sshdr.sense_key == ILLEGAL_REQUEST &&
+ (sshdr.asc == 0x20 || sshdr.asc == 0x24) &&
+ sshdr.ascq == 0x00)
+ /* Invalid Command Operation Code or
+ * Invalid Field in CDB, just retry
+ * silently with RC10 */
+ return -EINVAL;
}
-
+ retries--;
+
+ } while (the_result && retries);
+
+ if (the_result) {
+ sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY(16) failed\n");
+ read_capacity_error(sdkp, sdp, &sshdr, sense_valid, the_result);
+ return -EINVAL;
+ }
+
+ sector_size = (buffer[8] << 24) | (buffer[9] << 16) |
+ (buffer[10] << 8) | buffer[11];
+ lba = (((u64)buffer[0] << 56) | ((u64)buffer[1] << 48) |
+ ((u64)buffer[2] << 40) | ((u64)buffer[3] << 32) |
+ ((u64)buffer[4] << 24) | ((u64)buffer[5] << 16) |
+ ((u64)buffer[6] << 8) | (u64)buffer[7]);
+
+ sd_read_protection_type(sdkp, buffer);
+
+ if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
+ sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
+ "kernel compiled with support for large block "
+ "devices.\n");
+ sdkp->capacity = 0;
+ return -EOVERFLOW;
+ }
+
+ sdkp->capacity = lba + 1;
+ return sector_size;
+}
+
+static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp,
+ unsigned char *buffer)
+{
+ unsigned char cmd[16];
+ struct scsi_sense_hdr sshdr;
+ int sense_valid = 0;
+ int the_result;
+ int retries = 3;
+ sector_t lba;
+ unsigned sector_size;
+
+ do {
+ cmd[0] = READ_CAPACITY;
+ memset(&cmd[1], 0, 9);
+ memset(buffer, 0, 8);
+
the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
- buffer, longrc ? 13 : 8, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+ buffer, 8, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL);
if (media_not_present(sdkp, &sshdr))
- return;
+ return -ENODEV;
if (the_result)
sense_valid = scsi_sense_valid(&sshdr);
@@ -1316,85 +1400,96 @@ repeat:
} while (the_result && retries);
- if (the_result && !longrc) {
+ if (the_result) {
sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY failed\n");
- sd_print_result(sdkp, the_result);
- if (driver_byte(the_result) & DRIVER_SENSE)
- sd_print_sense_hdr(sdkp, &sshdr);
- else
- sd_printk(KERN_NOTICE, sdkp, "Sense not available.\n");
+ read_capacity_error(sdkp, sdp, &sshdr, sense_valid, the_result);
+ return -EINVAL;
+ }
- /* Set dirty bit for removable devices if not ready -
- * sometimes drives will not report this properly. */
- if (sdp->removable &&
- sense_valid && sshdr.sense_key == NOT_READY)
- sdp->changed = 1;
+ sector_size = (buffer[4] << 24) | (buffer[5] << 16) |
+ (buffer[6] << 8) | buffer[7];
+ lba = (buffer[0] << 24) | (buffer[1] << 16) |
+ (buffer[2] << 8) | buffer[3];
- /* Either no media are present but the drive didn't tell us,
- or they are present but the read capacity command fails */
- /* sdkp->media_present = 0; -- not always correct */
- sdkp->capacity = 0; /* unknown mapped to zero - as usual */
+ 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 "
+ "devices.\n");
+ sdkp->capacity = 0;
+ return -EOVERFLOW;
+ }
- return;
- } else if (the_result && longrc) {
- /* READ CAPACITY(16) has been failed */
- sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY(16) failed\n");
- sd_print_result(sdkp, the_result);
- sd_printk(KERN_NOTICE, sdkp, "Use 0xffffffff as device size\n");
+ sdkp->capacity = lba + 1;
+ return sector_size;
+}
- sdkp->capacity = 1 + (sector_t) 0xffffffff;
- goto got_data;
- }
-
- if (!longrc) {
- sector_size = (buffer[4] << 24) |
- (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
- if (buffer[0] == 0xff && buffer[1] == 0xff &&
- buffer[2] == 0xff && buffer[3] == 0xff) {
- if(sizeof(sdkp->capacity) > 4) {
- sd_printk(KERN_NOTICE, sdkp, "Very big device. "
- "Trying to use READ CAPACITY(16).\n");
- longrc = 1;
- goto repeat;
- }
- sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use "
- "a kernel compiled with support for large "
- "block devices.\n");
- sdkp->capacity = 0;
+static int sd_try_rc16_first(struct scsi_device *sdp)
+{
+ if (sdp->scsi_level > SCSI_SPC_2)
+ return 1;
+ if (scsi_device_protection(sdp))
+ return 1;
+ return 0;
+}
+
+/*
+ * read disk capacity
+ */
+static void
+sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)
+{
+ int sector_size;
+ struct scsi_device *sdp = sdkp->device;
+ sector_t old_capacity = sdkp->capacity;
+
+ if (sd_try_rc16_first(sdp)) {
+ sector_size = read_capacity_16(sdkp, sdp, buffer);
+ if (sector_size == -EOVERFLOW)
goto got_data;
- }
- sdkp->capacity = 1 + (((sector_t)buffer[0] << 24) |
- (buffer[1] << 16) |
- (buffer[2] << 8) |
- buffer[3]);
+ if (sector_size == -ENODEV)
+ return;
+ if (sector_size < 0)
+ sector_size = read_capacity_10(sdkp, sdp, buffer);
+ if (sector_size < 0)
+ return;
} else {
- sdkp->capacity = 1 + (((u64)buffer[0] << 56) |
- ((u64)buffer[1] << 48) |
- ((u64)buffer[2] << 40) |
- ((u64)buffer[3] << 32) |
- ((sector_t)buffer[4] << 24) |
- ((sector_t)buffer[5] << 16) |
- ((sector_t)buffer[6] << 8) |
- (sector_t)buffer[7]);
-
- sector_size = (buffer[8] << 24) |
- (buffer[9] << 16) | (buffer[10] << 8) | buffer[11];
-
- sd_read_protection_type(sdkp, buffer);
- }
-
- /* Some devices return the total number of sectors, not the
- * highest sector number. Make the necessary adjustment. */
- if (sdp->fix_capacity) {
- --sdkp->capacity;
+ sector_size = read_capacity_10(sdkp, sdp, buffer);
+ if (sector_size == -EOVERFLOW)
+ goto got_data;
+ if (sector_size < 0)
+ return;
+ if ((sizeof(sdkp->capacity) > 4) &&
+ (sdkp->capacity > 0xffffffffULL)) {
+ int old_sector_size = sector_size;
+ sd_printk(KERN_NOTICE, sdkp, "Very big device. "
+ "Trying to use READ CAPACITY(16).\n");
+ sector_size = read_capacity_16(sdkp, sdp, buffer);
+ if (sector_size < 0) {
+ sd_printk(KERN_NOTICE, sdkp,
+ "Using 0xffffffff as device size\n");
+ sdkp->capacity = 1 + (sector_t) 0xffffffff;
+ sector_size = old_sector_size;
+ goto got_data;
+ }
+ }
+ }
- /* Some devices have version which report the correct sizes
- * and others which do not. We guess size according to a heuristic
- * and err on the side of lowering the capacity. */
- } else {
- if (sdp->guess_capacity)
- if (sdkp->capacity & 0x01) /* odd sizes are odd */
- --sdkp->capacity;
+ /* Some devices are known to return the total number of blocks,
+ * not the highest block number. Some devices have versions
+ * which do this and others which do not. Some devices we might
+ * suspect of doing this but we don't know for certain.
+ *
+ * If we know the reported capacity is wrong, decrement it. If
+ * we can only guess, then assume the number of blocks is even
+ * (usually true but not always) and err on the side of lowering
+ * the capacity.
+ */
+ if (sdp->fix_capacity ||
+ (sdp->guess_capacity && (sdkp->capacity & 0x01))) {
+ sd_printk(KERN_INFO, sdkp, "Adjusting the sector count "
+ "from its reported value: %llu\n",
+ (unsigned long long) sdkp->capacity);
+ --sdkp->capacity;
}
got_data:
@@ -1437,10 +1532,11 @@ got_data:
string_get_size(sz, STRING_UNITS_10, cap_str_10,
sizeof(cap_str_10));
- sd_printk(KERN_NOTICE, sdkp,
- "%llu %d-byte hardware sectors: (%s/%s)\n",
- (unsigned long long)sdkp->capacity,
- sector_size, cap_str_10, cap_str_2);
+ if (sdkp->first_scan || old_capacity != sdkp->capacity)
+ sd_printk(KERN_NOTICE, sdkp,
+ "%llu %d-byte hardware sectors: (%s/%s)\n",
+ (unsigned long long)sdkp->capacity,
+ sector_size, cap_str_10, cap_str_2);
}
/* Rescale capacity to 512-byte units */
@@ -1477,6 +1573,7 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer)
int res;
struct scsi_device *sdp = sdkp->device;
struct scsi_mode_data data;
+ int old_wp = sdkp->write_prot;
set_disk_ro(sdkp->disk, 0);
if (sdp->skip_ms_page_3f) {
@@ -1517,11 +1614,13 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, unsigned char *buffer)
} else {
sdkp->write_prot = ((data.device_specific & 0x80) != 0);
set_disk_ro(sdkp->disk, sdkp->write_prot);
- sd_printk(KERN_NOTICE, sdkp, "Write Protect is %s\n",
- sdkp->write_prot ? "on" : "off");
- sd_printk(KERN_DEBUG, sdkp,
- "Mode Sense: %02x %02x %02x %02x\n",
- buffer[0], buffer[1], buffer[2], buffer[3]);
+ if (sdkp->first_scan || old_wp != sdkp->write_prot) {
+ sd_printk(KERN_NOTICE, sdkp, "Write Protect is %s\n",
+ sdkp->write_prot ? "on" : "off");
+ sd_printk(KERN_DEBUG, sdkp,
+ "Mode Sense: %02x %02x %02x %02x\n",
+ buffer[0], buffer[1], buffer[2], buffer[3]);
+ }
}
}
@@ -1539,6 +1638,9 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
int modepage;
struct scsi_mode_data data;
struct scsi_sense_hdr sshdr;
+ int old_wce = sdkp->WCE;
+ int old_rcd = sdkp->RCD;
+ int old_dpofua = sdkp->DPOFUA;
if (sdp->skip_ms_page_8)
goto defaults;
@@ -1610,12 +1712,14 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer)
sdkp->DPOFUA = 0;
}
- sd_printk(KERN_NOTICE, sdkp,
- "Write cache: %s, read cache: %s, %s\n",
- sdkp->WCE ? "enabled" : "disabled",
- sdkp->RCD ? "disabled" : "enabled",
- sdkp->DPOFUA ? "supports DPO and FUA"
- : "doesn't support DPO or FUA");
+ if (sdkp->first_scan || old_wce != sdkp->WCE ||
+ old_rcd != sdkp->RCD || old_dpofua != sdkp->DPOFUA)
+ sd_printk(KERN_NOTICE, sdkp,
+ "Write cache: %s, read cache: %s, %s\n",
+ sdkp->WCE ? "enabled" : "disabled",
+ sdkp->RCD ? "disabled" : "enabled",
+ sdkp->DPOFUA ? "supports DPO and FUA"
+ : "doesn't support DPO or FUA");
return;
}
@@ -1711,15 +1815,6 @@ static int sd_revalidate_disk(struct gendisk *disk)
goto out;
}
- /* defaults, until the device tells us otherwise */
- sdp->sector_size = 512;
- sdkp->capacity = 0;
- sdkp->media_present = 1;
- sdkp->write_prot = 0;
- sdkp->WCE = 0;
- sdkp->RCD = 0;
- sdkp->ATO = 0;
-
sd_spinup_disk(sdkp);
/*
@@ -1733,6 +1828,8 @@ static int sd_revalidate_disk(struct gendisk *disk)
sd_read_app_tag_own(sdkp, buffer);
}
+ sdkp->first_scan = 0;
+
/*
* We now have all cache related info, determine how we deal
* with ordered requests. Note that as the current SCSI
@@ -1843,6 +1940,16 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
gd->private_data = &sdkp->driver;
gd->queue = sdkp->device->request_queue;
+ /* defaults, until the device tells us otherwise */
+ sdp->sector_size = 512;
+ sdkp->capacity = 0;
+ sdkp->media_present = 1;
+ sdkp->write_prot = 0;
+ sdkp->WCE = 0;
+ sdkp->RCD = 0;
+ sdkp->ATO = 0;
+ sdkp->first_scan = 1;
+
sd_revalidate_disk(gd);
blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);