hisi_sas: Restrict SCSI_HISI_SAS to arm64
[deliverable/linux.git] / drivers / scsi / storvsc_drv.c
index 3fba42ad9fb8aee55d5015167a9cb9ad601fb1a7..41c115c230d9bf66da94693597967d037fdefaf6 100644 (file)
@@ -41,6 +41,7 @@
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_devinfo.h>
 #include <scsi/scsi_dbg.h>
+#include <scsi/scsi_transport_fc.h>
 
 /*
  * All wire protocol details (storage protocol between the guest and the host)
@@ -92,9 +93,8 @@ enum vstor_packet_operation {
  */
 
 struct hv_fc_wwn_packet {
-       bool    primary_active;
-       u8      reserved1;
-       u8      reserved2;
+       u8      primary_active;
+       u8      reserved1[3];
        u8      primary_port_wwn[8];
        u8      primary_node_wwn[8];
        u8      secondary_port_wwn[8];
@@ -164,6 +164,26 @@ static int sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE;
 */
 static int vmstor_proto_version;
 
+#define STORVSC_LOGGING_NONE   0
+#define STORVSC_LOGGING_ERROR  1
+#define STORVSC_LOGGING_WARN   2
+
+static int logging_level = STORVSC_LOGGING_ERROR;
+module_param(logging_level, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(logging_level,
+       "Logging level, 0 - None, 1 - Error (default), 2 - Warning.");
+
+static inline bool do_logging(int level)
+{
+       return logging_level >= level;
+}
+
+#define storvsc_log(dev, level, fmt, ...)                      \
+do {                                                           \
+       if (do_logging(level))                                  \
+               dev_warn(&(dev)->device, fmt, ##__VA_ARGS__);   \
+} while (0)
+
 struct vmscsi_win8_extension {
        /*
         * The following were added in Windows 8
@@ -378,6 +398,9 @@ static int storvsc_timeout = 180;
 
 static int msft_blist_flags = BLIST_TRY_VPD_PAGES;
 
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+static struct scsi_transport_template *fc_transport_template;
+#endif
 
 static void storvsc_on_channel_callback(void *context);
 
@@ -437,6 +460,11 @@ struct storvsc_device {
        /* Used for vsc/vsp channel reset process */
        struct storvsc_cmd_request init_request;
        struct storvsc_cmd_request reset_request;
+       /*
+        * Currently active port and node names for FC devices.
+        */
+       u64 node_name;
+       u64 port_name;
 };
 
 struct hv_host_device {
@@ -676,29 +704,36 @@ static void  handle_multichannel_storage(struct hv_device *device, int max_chns)
        vmbus_are_subchannels_present(device->channel);
 }
 
-static int storvsc_channel_init(struct hv_device *device)
+static void cache_wwn(struct storvsc_device *stor_device,
+                     struct vstor_packet *vstor_packet)
 {
-       struct storvsc_device *stor_device;
-       struct storvsc_cmd_request *request;
-       struct vstor_packet *vstor_packet;
-       int ret, t, i;
-       int max_chns;
-       bool process_sub_channels = false;
+       /*
+        * Cache the currently active port and node ww names.
+        */
+       if (vstor_packet->wwn_packet.primary_active) {
+               stor_device->node_name =
+                       wwn_to_u64(vstor_packet->wwn_packet.primary_node_wwn);
+               stor_device->port_name =
+                       wwn_to_u64(vstor_packet->wwn_packet.primary_port_wwn);
+       } else {
+               stor_device->node_name =
+                       wwn_to_u64(vstor_packet->wwn_packet.secondary_node_wwn);
+               stor_device->port_name =
+                       wwn_to_u64(vstor_packet->wwn_packet.secondary_port_wwn);
+       }
+}
 
-       stor_device = get_out_stor_device(device);
-       if (!stor_device)
-               return -ENODEV;
 
-       request = &stor_device->init_request;
+static int storvsc_execute_vstor_op(struct hv_device *device,
+                                   struct storvsc_cmd_request *request,
+                                   bool status_check)
+{
+       struct vstor_packet *vstor_packet;
+       int ret, t;
+
        vstor_packet = &request->vstor_packet;
 
-       /*
-        * Now, initiate the vsc/vsp initialization protocol on the open
-        * channel
-        */
-       memset(request, 0, sizeof(struct storvsc_cmd_request));
        init_completion(&request->wait_event);
-       vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION;
        vstor_packet->flags = REQUEST_COMPLETION_FLAG;
 
        ret = vmbus_sendpacket(device->channel, vstor_packet,
@@ -708,27 +743,56 @@ static int storvsc_channel_init(struct hv_device *device)
                               VM_PKT_DATA_INBAND,
                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
        if (ret != 0)
-               goto cleanup;
+               return ret;
 
        t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
-       if (t == 0) {
-               ret = -ETIMEDOUT;
-               goto cleanup;
-       }
+       if (t == 0)
+               return -ETIMEDOUT;
+
+       if (!status_check)
+               return ret;
 
        if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
-           vstor_packet->status != 0) {
-               ret = -EINVAL;
-               goto cleanup;
-       }
+           vstor_packet->status != 0)
+               return -EINVAL;
+
+       return ret;
+}
+
+static int storvsc_channel_init(struct hv_device *device, bool is_fc)
+{
+       struct storvsc_device *stor_device;
+       struct storvsc_cmd_request *request;
+       struct vstor_packet *vstor_packet;
+       int ret, i;
+       int max_chns;
+       bool process_sub_channels = false;
 
+       stor_device = get_out_stor_device(device);
+       if (!stor_device)
+               return -ENODEV;
+
+       request = &stor_device->init_request;
+       vstor_packet = &request->vstor_packet;
+
+       /*
+        * Now, initiate the vsc/vsp initialization protocol on the open
+        * channel
+        */
+       memset(request, 0, sizeof(struct storvsc_cmd_request));
+       vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION;
+       ret = storvsc_execute_vstor_op(device, request, true);
+       if (ret)
+               return ret;
+       /*
+        * Query host supported protocol version.
+        */
 
        for (i = 0; i < ARRAY_SIZE(vmstor_protocols); i++) {
                /* reuse the packet for version range supported */
                memset(vstor_packet, 0, sizeof(struct vstor_packet));
                vstor_packet->operation =
                        VSTOR_OPERATION_QUERY_PROTOCOL_VERSION;
-               vstor_packet->flags = REQUEST_COMPLETION_FLAG;
 
                vstor_packet->version.major_minor =
                        vmstor_protocols[i].protocol_version;
@@ -737,26 +801,12 @@ static int storvsc_channel_init(struct hv_device *device)
                 * The revision number is only used in Windows; set it to 0.
                 */
                vstor_packet->version.revision = 0;
-
-               ret = vmbus_sendpacket(device->channel, vstor_packet,
-                              (sizeof(struct vstor_packet) -
-                               vmscsi_size_delta),
-                              (unsigned long)request,
-                              VM_PKT_DATA_INBAND,
-                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+               ret = storvsc_execute_vstor_op(device, request, false);
                if (ret != 0)
-                       goto cleanup;
+                       return ret;
 
-               t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
-               if (t == 0) {
-                       ret = -ETIMEDOUT;
-                       goto cleanup;
-               }
-
-               if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO) {
-                       ret = -EINVAL;
-                       goto cleanup;
-               }
+               if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO)
+                       return -EINVAL;
 
                if (vstor_packet->status == 0) {
                        vmstor_proto_version =
@@ -772,37 +822,15 @@ static int storvsc_channel_init(struct hv_device *device)
                }
        }
 
-       if (vstor_packet->status != 0) {
-               ret = -EINVAL;
-               goto cleanup;
-       }
+       if (vstor_packet->status != 0)
+               return -EINVAL;
 
 
        memset(vstor_packet, 0, sizeof(struct vstor_packet));
        vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES;
-       vstor_packet->flags = REQUEST_COMPLETION_FLAG;
-
-       ret = vmbus_sendpacket(device->channel, vstor_packet,
-                              (sizeof(struct vstor_packet) -
-                               vmscsi_size_delta),
-                              (unsigned long)request,
-                              VM_PKT_DATA_INBAND,
-                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
-
+       ret = storvsc_execute_vstor_op(device, request, true);
        if (ret != 0)
-               goto cleanup;
-
-       t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
-       if (t == 0) {
-               ret = -ETIMEDOUT;
-               goto cleanup;
-       }
-
-       if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
-           vstor_packet->status != 0) {
-               ret = -EINVAL;
-               goto cleanup;
-       }
+               return ret;
 
        /*
         * Check to see if multi-channel support is there.
@@ -818,37 +846,34 @@ static int storvsc_channel_init(struct hv_device *device)
        stor_device->max_transfer_bytes =
                vstor_packet->storage_channel_properties.max_transfer_bytes;
 
-       memset(vstor_packet, 0, sizeof(struct vstor_packet));
-       vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION;
-       vstor_packet->flags = REQUEST_COMPLETION_FLAG;
-
-       ret = vmbus_sendpacket(device->channel, vstor_packet,
-                              (sizeof(struct vstor_packet) -
-                               vmscsi_size_delta),
-                              (unsigned long)request,
-                              VM_PKT_DATA_INBAND,
-                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (!is_fc)
+               goto done;
 
+       /*
+        * For FC devices retrieve FC HBA data.
+        */
+       memset(vstor_packet, 0, sizeof(struct vstor_packet));
+       vstor_packet->operation = VSTOR_OPERATION_FCHBA_DATA;
+       ret = storvsc_execute_vstor_op(device, request, true);
        if (ret != 0)
-               goto cleanup;
+               return ret;
 
-       t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
-       if (t == 0) {
-               ret = -ETIMEDOUT;
-               goto cleanup;
-       }
+       /*
+        * Cache the currently active port and node ww names.
+        */
+       cache_wwn(stor_device, vstor_packet);
 
-       if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
-           vstor_packet->status != 0) {
-               ret = -EINVAL;
-               goto cleanup;
-       }
+done:
+
+       memset(vstor_packet, 0, sizeof(struct vstor_packet));
+       vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION;
+       ret = storvsc_execute_vstor_op(device, request, true);
+       if (ret != 0)
+               return ret;
 
        if (process_sub_channels)
                handle_multichannel_storage(device, max_chns);
 
-
-cleanup:
        return ret;
 }
 
@@ -920,19 +945,16 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
 }
 
 
-static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
+static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request,
+                                      struct storvsc_device *stor_dev)
 {
        struct scsi_cmnd *scmnd = cmd_request->cmd;
-       struct hv_host_device *host_dev = shost_priv(scmnd->device->host);
        struct scsi_sense_hdr sense_hdr;
        struct vmscsi_request *vm_srb;
        struct Scsi_Host *host;
-       struct storvsc_device *stor_dev;
-       struct hv_device *dev = host_dev->dev;
        u32 payload_sz = cmd_request->payload_sz;
        void *payload = cmd_request->payload;
 
-       stor_dev = get_in_stor_device(dev);
        host = stor_dev->host;
 
        vm_srb = &cmd_request->vstor_packet.vm_srb;
@@ -941,7 +963,8 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
 
        if (scmnd->result) {
                if (scsi_normalize_sense(scmnd->sense_buffer,
-                               SCSI_SENSE_BUFFERSIZE, &sense_hdr))
+                               SCSI_SENSE_BUFFERSIZE, &sense_hdr) &&
+                   do_logging(STORVSC_LOGGING_ERROR))
                        scsi_print_sense_hdr(scmnd->device, "storvsc",
                                             &sense_hdr);
        }
@@ -961,14 +984,13 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
                kfree(payload);
 }
 
-static void storvsc_on_io_completion(struct hv_device *device,
+static void storvsc_on_io_completion(struct storvsc_device *stor_device,
                                  struct vstor_packet *vstor_packet,
                                  struct storvsc_cmd_request *request)
 {
-       struct storvsc_device *stor_device;
        struct vstor_packet *stor_pkt;
+       struct hv_device *device = stor_device->device;
 
-       stor_device = hv_get_drvdata(device);
        stor_pkt = &request->vstor_packet;
 
        /*
@@ -995,6 +1017,13 @@ static void storvsc_on_io_completion(struct hv_device *device,
        stor_pkt->vm_srb.sense_info_length =
        vstor_packet->vm_srb.sense_info_length;
 
+       if (vstor_packet->vm_srb.scsi_status != 0 ||
+           vstor_packet->vm_srb.srb_status != SRB_STATUS_SUCCESS)
+               storvsc_log(device, STORVSC_LOGGING_WARN,
+                       "cmd 0x%x scsi status 0x%x srb status 0x%x\n",
+                       stor_pkt->vm_srb.cdb[0],
+                       vstor_packet->vm_srb.scsi_status,
+                       vstor_packet->vm_srb.srb_status);
 
        if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) {
                /* CHECK_CONDITION */
@@ -1002,6 +1031,10 @@ static void storvsc_on_io_completion(struct hv_device *device,
                        SRB_STATUS_AUTOSENSE_VALID) {
                        /* autosense data available */
 
+                       storvsc_log(device, STORVSC_LOGGING_WARN,
+                               "stor pkt %p autosense data valid - len %d\n",
+                               request, vstor_packet->vm_srb.sense_info_length);
+
                        memcpy(request->cmd->sense_buffer,
                               vstor_packet->vm_srb.sense_data,
                               vstor_packet->vm_srb.sense_info_length);
@@ -1012,7 +1045,7 @@ static void storvsc_on_io_completion(struct hv_device *device,
        stor_pkt->vm_srb.data_transfer_length =
        vstor_packet->vm_srb.data_transfer_length;
 
-       storvsc_command_completion(request);
+       storvsc_command_completion(request, stor_device);
 
        if (atomic_dec_and_test(&stor_device->num_outstanding_req) &&
                stor_device->drain_notify)
@@ -1021,21 +1054,19 @@ static void storvsc_on_io_completion(struct hv_device *device,
 
 }
 
-static void storvsc_on_receive(struct hv_device *device,
+static void storvsc_on_receive(struct storvsc_device *stor_device,
                             struct vstor_packet *vstor_packet,
                             struct storvsc_cmd_request *request)
 {
        struct storvsc_scan_work *work;
-       struct storvsc_device *stor_device;
 
        switch (vstor_packet->operation) {
        case VSTOR_OPERATION_COMPLETE_IO:
-               storvsc_on_io_completion(device, vstor_packet, request);
+               storvsc_on_io_completion(stor_device, vstor_packet, request);
                break;
 
        case VSTOR_OPERATION_REMOVE_DEVICE:
        case VSTOR_OPERATION_ENUMERATE_BUS:
-               stor_device = get_in_stor_device(device);
                work = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC);
                if (!work)
                        return;
@@ -1045,6 +1076,13 @@ static void storvsc_on_receive(struct hv_device *device,
                schedule_work(&work->work);
                break;
 
+       case VSTOR_OPERATION_FCHBA_DATA:
+               cache_wwn(stor_device, vstor_packet);
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+               fc_host_node_name(stor_device->host) = stor_device->node_name;
+               fc_host_port_name(stor_device->host) = stor_device->port_name;
+#endif
+               break;
        default:
                break;
        }
@@ -1088,7 +1126,7 @@ static void storvsc_on_channel_callback(void *context)
                                        vmscsi_size_delta));
                                complete(&request->wait_event);
                        } else {
-                               storvsc_on_receive(device,
+                               storvsc_on_receive(stor_device,
                                                (struct vstor_packet *)packet,
                                                request);
                        }
@@ -1100,7 +1138,8 @@ static void storvsc_on_channel_callback(void *context)
        return;
 }
 
-static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size)
+static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size,
+                                 bool is_fc)
 {
        struct vmstorage_channel_properties props;
        int ret;
@@ -1117,7 +1156,7 @@ static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size)
        if (ret != 0)
                return ret;
 
-       ret = storvsc_channel_init(device);
+       ret = storvsc_channel_init(device, is_fc);
 
        return ret;
 }
@@ -1542,6 +1581,7 @@ static int storvsc_probe(struct hv_device *device,
        struct Scsi_Host *host;
        struct hv_host_device *host_dev;
        bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false);
+       bool is_fc = ((dev_id->driver_data == SFC_GUID) ? true : false);
        int target = 0;
        struct storvsc_device *stor_device;
        int max_luns_per_target;
@@ -1599,7 +1639,7 @@ static int storvsc_probe(struct hv_device *device,
        hv_set_drvdata(device, stor_device);
 
        stor_device->port_number = host->host_no;
-       ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size);
+       ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size, is_fc);
        if (ret)
                goto err_out1;
 
@@ -1611,6 +1651,9 @@ static int storvsc_probe(struct hv_device *device,
                host->max_lun = STORVSC_FC_MAX_LUNS_PER_TARGET;
                host->max_id = STORVSC_FC_MAX_TARGETS;
                host->max_channel = STORVSC_FC_MAX_CHANNELS - 1;
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+               host->transportt = fc_transport_template;
+#endif
                break;
 
        case SCSI_GUID:
@@ -1650,6 +1693,12 @@ static int storvsc_probe(struct hv_device *device,
                        goto err_out2;
                }
        }
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+       if (host->transportt == fc_transport_template) {
+               fc_host_node_name(host) = stor_device->node_name;
+               fc_host_port_name(host) = stor_device->port_name;
+       }
+#endif
        return 0;
 
 err_out2:
@@ -1675,6 +1724,10 @@ static int storvsc_remove(struct hv_device *dev)
        struct storvsc_device *stor_device = hv_get_drvdata(dev);
        struct Scsi_Host *host = stor_device->host;
 
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+       if (host->transportt == fc_transport_template)
+               fc_remove_host(host);
+#endif
        scsi_remove_host(host);
        storvsc_dev_remove(dev);
        scsi_host_put(host);
@@ -1689,8 +1742,16 @@ static struct hv_driver storvsc_drv = {
        .remove = storvsc_remove,
 };
 
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+static struct fc_function_template fc_transport_functions = {
+       .show_host_node_name = 1,
+       .show_host_port_name = 1,
+};
+#endif
+
 static int __init storvsc_drv_init(void)
 {
+       int ret;
 
        /*
         * Divide the ring buffer data size (which is 1 page less
@@ -1705,12 +1766,28 @@ static int __init storvsc_drv_init(void)
                vmscsi_size_delta,
                sizeof(u64)));
 
-       return vmbus_driver_register(&storvsc_drv);
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+       fc_transport_template = fc_attach_transport(&fc_transport_functions);
+       if (!fc_transport_template)
+               return -ENODEV;
+#endif
+
+       ret = vmbus_driver_register(&storvsc_drv);
+
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+       if (ret)
+               fc_release_transport(fc_transport_template);
+#endif
+
+       return ret;
 }
 
 static void __exit storvsc_drv_exit(void)
 {
        vmbus_driver_unregister(&storvsc_drv);
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+       fc_release_transport(fc_transport_template);
+#endif
 }
 
 MODULE_LICENSE("GPL");
This page took 0.08748 seconds and 5 git commands to generate.