]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/scsi/storvsc_drv.c
drivers: scsi: storvsc: Correctly handle TEST_UNIT_READY failure
[karo-tx-linux.git] / drivers / scsi / storvsc_drv.c
index 9969fa1ef7c4ef09ac4fc72c942c5ef72703ae49..ed0f899e8aa56cf39dcc639129a5ecf1713d0011 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/device.h>
 #include <linux/hyperv.h>
 #include <linux/mempool.h>
+#include <linux/blkdev.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_host.h>
@@ -330,17 +331,17 @@ static int storvsc_timeout = 180;
 
 static void storvsc_on_channel_callback(void *context);
 
-/*
- * In Hyper-V, each port/path/target maps to 1 scsi host adapter.  In
- * reality, the path/target is not used (ie always set to 0) so our
- * scsi host adapter essentially has 1 bus with 1 target that contains
- * up to 256 luns.
- */
-#define STORVSC_MAX_LUNS_PER_TARGET                    64
-#define STORVSC_MAX_TARGETS                            1
-#define STORVSC_MAX_CHANNELS                           1
+#define STORVSC_MAX_LUNS_PER_TARGET                    255
+#define STORVSC_MAX_TARGETS                            2
+#define STORVSC_MAX_CHANNELS                           8
 
+#define STORVSC_FC_MAX_LUNS_PER_TARGET                 255
+#define STORVSC_FC_MAX_TARGETS                         128
+#define STORVSC_FC_MAX_CHANNELS                                8
 
+#define STORVSC_IDE_MAX_LUNS_PER_TARGET                        64
+#define STORVSC_IDE_MAX_TARGETS                                1
+#define STORVSC_IDE_MAX_CHANNELS                       1
 
 struct storvsc_cmd_request {
        struct list_head entry;
@@ -1017,6 +1018,13 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
                case ATA_12:
                        set_host_byte(scmnd, DID_PASSTHROUGH);
                        break;
+               /*
+                * On Some Windows hosts TEST_UNIT_READY command can return
+                * SRB_STATUS_ERROR, let the upper level code deal with it
+                * based on the sense information.
+                */
+               case TEST_UNIT_READY:
+                       break;
                default:
                        set_host_byte(scmnd, DID_TARGET_FAILURE);
                }
@@ -1518,6 +1526,16 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd)
        return SUCCESS;
 }
 
+/*
+ * The host guarantees to respond to each command, although I/O latencies might
+ * be unbounded on Azure.  Reset the timer unconditionally to give the host a
+ * chance to perform EH.
+ */
+static enum blk_eh_timer_return storvsc_eh_timed_out(struct scsi_cmnd *scmnd)
+{
+       return BLK_EH_RESET_TIMER;
+}
+
 static bool storvsc_scsi_cmd_ok(struct scsi_cmnd *scmnd)
 {
        bool allowed = true;
@@ -1553,9 +1571,19 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
        struct vmscsi_request *vm_srb;
        struct stor_mem_pools *memp = scmnd->device->hostdata;
 
-       if (!storvsc_scsi_cmd_ok(scmnd)) {
-               scmnd->scsi_done(scmnd);
-               return 0;
+       if (vmstor_current_major <= VMSTOR_WIN8_MAJOR) {
+               /*
+                * On legacy hosts filter unimplemented commands.
+                * Future hosts are expected to correctly handle
+                * unsupported commands. Furthermore, it is
+                * possible that some of the currently
+                * unsupported commands maybe supported in
+                * future versions of the host.
+                */
+               if (!storvsc_scsi_cmd_ok(scmnd)) {
+                       scmnd->scsi_done(scmnd);
+                       return 0;
+               }
        }
 
        request_size = sizeof(struct storvsc_cmd_request);
@@ -1580,26 +1608,24 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
        vm_srb = &cmd_request->vstor_packet.vm_srb;
        vm_srb->win8_extension.time_out_value = 60;
 
+       vm_srb->win8_extension.srb_flags |=
+               (SRB_FLAGS_QUEUE_ACTION_ENABLE |
+               SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
 
        /* Build the SRB */
        switch (scmnd->sc_data_direction) {
        case DMA_TO_DEVICE:
                vm_srb->data_in = WRITE_TYPE;
                vm_srb->win8_extension.srb_flags |= SRB_FLAGS_DATA_OUT;
-               vm_srb->win8_extension.srb_flags |=
-                       (SRB_FLAGS_QUEUE_ACTION_ENABLE |
-                       SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
                break;
        case DMA_FROM_DEVICE:
                vm_srb->data_in = READ_TYPE;
                vm_srb->win8_extension.srb_flags |= SRB_FLAGS_DATA_IN;
-               vm_srb->win8_extension.srb_flags |=
-                       (SRB_FLAGS_QUEUE_ACTION_ENABLE |
-                       SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
                break;
        default:
                vm_srb->data_in = UNKNOWN_TYPE;
-               vm_srb->win8_extension.srb_flags = 0;
+               vm_srb->win8_extension.srb_flags |= (SRB_FLAGS_DATA_IN |
+                                                    SRB_FLAGS_DATA_OUT);
                break;
        }
 
@@ -1687,11 +1713,11 @@ static struct scsi_host_template scsi_driver = {
        .bios_param =           storvsc_get_chs,
        .queuecommand =         storvsc_queuecommand,
        .eh_host_reset_handler =        storvsc_host_reset_handler,
+       .eh_timed_out =         storvsc_eh_timed_out,
        .slave_alloc =          storvsc_device_alloc,
        .slave_destroy =        storvsc_device_destroy,
        .slave_configure =      storvsc_device_configure,
-       .cmd_per_lun =          1,
-       /* 64 max_queue * 1 target */
+       .cmd_per_lun =          255,
        .can_queue =            STORVSC_MAX_IO_REQUESTS*STORVSC_MAX_TARGETS,
        .this_id =              -1,
        /* no use setting to 0 since ll_blk_rw reset it to 1 */
@@ -1743,19 +1769,25 @@ static int storvsc_probe(struct hv_device *device,
         * set state to properly communicate with the host.
         */
 
-       if (vmbus_proto_version == VERSION_WIN8) {
-               sense_buffer_size = POST_WIN7_STORVSC_SENSE_BUFFER_SIZE;
-               vmscsi_size_delta = 0;
-               vmstor_current_major = VMSTOR_WIN8_MAJOR;
-               vmstor_current_minor = VMSTOR_WIN8_MINOR;
-       } else {
+       switch (vmbus_proto_version) {
+       case VERSION_WS2008:
+       case VERSION_WIN7:
                sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE;
                vmscsi_size_delta = sizeof(struct vmscsi_win8_extension);
                vmstor_current_major = VMSTOR_WIN7_MAJOR;
                vmstor_current_minor = VMSTOR_WIN7_MINOR;
+               break;
+       default:
+               sense_buffer_size = POST_WIN7_STORVSC_SENSE_BUFFER_SIZE;
+               vmscsi_size_delta = 0;
+               vmstor_current_major = VMSTOR_WIN8_MAJOR;
+               vmstor_current_minor = VMSTOR_WIN8_MINOR;
+               break;
        }
 
-
+       if (dev_id->driver_data == SFC_GUID)
+               scsi_driver.can_queue = (STORVSC_MAX_IO_REQUESTS *
+                                        STORVSC_FC_MAX_TARGETS);
        host = scsi_host_alloc(&scsi_driver,
                               sizeof(struct hv_host_device));
        if (!host)
@@ -1789,12 +1821,25 @@ static int storvsc_probe(struct hv_device *device,
        host_dev->path = stor_device->path_id;
        host_dev->target = stor_device->target_id;
 
-       /* max # of devices per target */
-       host->max_lun = STORVSC_MAX_LUNS_PER_TARGET;
-       /* max # of targets per channel */
-       host->max_id = STORVSC_MAX_TARGETS;
-       /* max # of channels */
-       host->max_channel = STORVSC_MAX_CHANNELS - 1;
+       switch (dev_id->driver_data) {
+       case SFC_GUID:
+               host->max_lun = STORVSC_FC_MAX_LUNS_PER_TARGET;
+               host->max_id = STORVSC_FC_MAX_TARGETS;
+               host->max_channel = STORVSC_FC_MAX_CHANNELS - 1;
+               break;
+
+       case SCSI_GUID:
+               host->max_lun = STORVSC_MAX_LUNS_PER_TARGET;
+               host->max_id = STORVSC_MAX_TARGETS;
+               host->max_channel = STORVSC_MAX_CHANNELS - 1;
+               break;
+
+       default:
+               host->max_lun = STORVSC_IDE_MAX_LUNS_PER_TARGET;
+               host->max_id = STORVSC_IDE_MAX_TARGETS;
+               host->max_channel = STORVSC_IDE_MAX_CHANNELS - 1;
+               break;
+       }
        /* max cmd length */
        host->max_cmd_len = STORVSC_MAX_CMD_LEN;