summaryrefslogtreecommitdiff
path: root/drivers/scsi/storvsc_drv.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-07-14 00:41:21 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-14 00:41:21 (GMT)
commit03ce3ca4b02bfc1e6567a7851ae231ad3cc9418e (patch)
treebf953469c6cac6b0a5b7d3671a27473eace144e3 /drivers/scsi/storvsc_drv.c
parentf8acc450e10dbe7996220bac5459ee9c14a82a6a (diff)
parent622f9a8e7b8043a5ea2c7d047d65ecad01fe0f97 (diff)
downloadlinux-fsl-qoriq-03ce3ca4b02bfc1e6567a7851ae231ad3cc9418e.tar.xz
Merge tag 'scsi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull final round of SCSI updates from James Bottomley: "This is the remaining set of SCSI patches for the merge window. It's mostly driver updates (scsi_debug, qla2xxx, storvsc, mp3sas). There are also several bug fixes in fcoe, libfc, and megaraid_sas. We also have a couple of core changes to try to make device destruction more deterministic" * tag 'scsi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (46 commits) [SCSI] scsi constants: command, sense key + additional sense strings fcoe: Reduce number of sparse warnings fcoe: Stop fc_rport_priv structure leak libfcoe: Fix meaningless log statement libfc: Differentiate echange timer cancellation debug statements libfc: Remove extra space in fc_exch_timer_cancel definition fcoe: fix the link error status block sparse warnings fcoe: Fix smatch warning in fcoe_fdmi_info function libfc: Reject PLOGI from nodes with incompatible role [SCSI] enable destruction of blocked devices which fail LUN scanning [SCSI] Fix race between starved list and device removal [SCSI] megaraid_sas: fix a bug for 64 bit arches [SCSI] scsi_debug: reduce duplication between prot_verify_read and prot_verify_write [SCSI] scsi_debug: simplify offset calculation for dif_storep [SCSI] scsi_debug: invalidate protection info for unmapped region [SCSI] scsi_debug: fix NULL pointer dereference with parameters dif=0 dix=1 [SCSI] scsi_debug: fix incorrectly nested kmap_atomic() [SCSI] scsi_debug: fix invalid address passed to kunmap_atomic() [SCSI] mpt3sas: Bump driver version to v02.100.00.00 [SCSI] mpt3sas: when async scanning is enabled then while scanning, devices are removed but their transport layer entries are not removed ...
Diffstat (limited to 'drivers/scsi/storvsc_drv.c')
-rw-r--r--drivers/scsi/storvsc_drv.c139
1 files changed, 134 insertions, 5 deletions
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 9451989..83ec1aa 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -326,7 +326,9 @@ MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)");
*/
static int storvsc_timeout = 180;
-#define STORVSC_MAX_IO_REQUESTS 128
+#define STORVSC_MAX_IO_REQUESTS 200
+
+static void storvsc_on_channel_callback(void *context);
/*
* In Hyper-V, each port/path/target maps to 1 scsi host adapter. In
@@ -364,6 +366,7 @@ struct storvsc_device {
bool destroy;
bool drain_notify;
+ bool open_sub_channel;
atomic_t num_outstanding_req;
struct Scsi_Host *host;
@@ -755,12 +758,104 @@ static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl,
return total_copied;
}
+static void handle_sc_creation(struct vmbus_channel *new_sc)
+{
+ struct hv_device *device = new_sc->primary_channel->device_obj;
+ struct storvsc_device *stor_device;
+ struct vmstorage_channel_properties props;
+
+ stor_device = get_out_stor_device(device);
+ if (!stor_device)
+ return;
+
+ if (stor_device->open_sub_channel == false)
+ return;
+
+ memset(&props, 0, sizeof(struct vmstorage_channel_properties));
+
+ vmbus_open(new_sc,
+ storvsc_ringbuffer_size,
+ storvsc_ringbuffer_size,
+ (void *)&props,
+ sizeof(struct vmstorage_channel_properties),
+ storvsc_on_channel_callback, new_sc);
+}
+
+static void handle_multichannel_storage(struct hv_device *device, int max_chns)
+{
+ struct storvsc_device *stor_device;
+ int num_cpus = num_online_cpus();
+ int num_sc;
+ struct storvsc_cmd_request *request;
+ struct vstor_packet *vstor_packet;
+ int ret, t;
+
+ num_sc = ((max_chns > num_cpus) ? num_cpus : max_chns);
+ stor_device = get_out_stor_device(device);
+ if (!stor_device)
+ return;
+
+ request = &stor_device->init_request;
+ vstor_packet = &request->vstor_packet;
+
+ stor_device->open_sub_channel = true;
+ /*
+ * Establish a handler for dealing with subchannels.
+ */
+ vmbus_set_sc_create_callback(device->channel, handle_sc_creation);
+
+ /*
+ * Check to see if sub-channels have already been created. This
+ * can happen when this driver is re-loaded after unloading.
+ */
+
+ if (vmbus_are_subchannels_present(device->channel))
+ return;
+
+ stor_device->open_sub_channel = false;
+ /*
+ * Request the host to create sub-channels.
+ */
+ memset(request, 0, sizeof(struct storvsc_cmd_request));
+ init_completion(&request->wait_event);
+ vstor_packet->operation = VSTOR_OPERATION_CREATE_SUB_CHANNELS;
+ vstor_packet->flags = REQUEST_COMPLETION_FLAG;
+ vstor_packet->sub_channel_count = num_sc;
+
+ 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 (ret != 0)
+ return;
+
+ t = wait_for_completion_timeout(&request->wait_event, 10*HZ);
+ if (t == 0)
+ return;
+
+ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
+ vstor_packet->status != 0)
+ return;
+
+ /*
+ * Now that we created the sub-channels, invoke the check; this
+ * may trigger the callback.
+ */
+ stor_device->open_sub_channel = true;
+ vmbus_are_subchannels_present(device->channel);
+}
+
static int storvsc_channel_init(struct hv_device *device)
{
struct storvsc_device *stor_device;
struct storvsc_cmd_request *request;
struct vstor_packet *vstor_packet;
int ret, t;
+ int max_chns;
+ bool process_sub_channels = false;
stor_device = get_out_stor_device(device);
if (!stor_device)
@@ -855,6 +950,19 @@ static int storvsc_channel_init(struct hv_device *device)
vstor_packet->status != 0)
goto cleanup;
+ /*
+ * Check to see if multi-channel support is there.
+ * Hosts that implement protocol version of 5.1 and above
+ * support multi-channel.
+ */
+ max_chns = vstor_packet->storage_channel_properties.max_channel_cnt;
+ if ((vmbus_proto_version != VERSION_WIN7) &&
+ (vmbus_proto_version != VERSION_WS2008)) {
+ if (vstor_packet->storage_channel_properties.flags &
+ STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL)
+ process_sub_channels = true;
+ }
+
memset(vstor_packet, 0, sizeof(struct vstor_packet));
vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION;
vstor_packet->flags = REQUEST_COMPLETION_FLAG;
@@ -879,6 +987,9 @@ static int storvsc_channel_init(struct hv_device *device)
vstor_packet->status != 0)
goto cleanup;
+ if (process_sub_channels)
+ handle_multichannel_storage(device, max_chns);
+
cleanup:
return ret;
@@ -1100,7 +1211,8 @@ static void storvsc_on_receive(struct hv_device *device,
static void storvsc_on_channel_callback(void *context)
{
- struct hv_device *device = (struct hv_device *)context;
+ struct vmbus_channel *channel = (struct vmbus_channel *)context;
+ struct hv_device *device;
struct storvsc_device *stor_device;
u32 bytes_recvd;
u64 request_id;
@@ -1108,13 +1220,17 @@ static void storvsc_on_channel_callback(void *context)
struct storvsc_cmd_request *request;
int ret;
+ if (channel->primary_channel != NULL)
+ device = channel->primary_channel->device_obj;
+ else
+ device = channel->device_obj;
stor_device = get_in_stor_device(device);
if (!stor_device)
return;
do {
- ret = vmbus_recvpacket(device->channel, packet,
+ ret = vmbus_recvpacket(channel, packet,
ALIGN((sizeof(struct vstor_packet) -
vmscsi_size_delta), 8),
&bytes_recvd, &request_id);
@@ -1155,7 +1271,7 @@ static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size)
ring_size,
(void *)&props,
sizeof(struct vmstorage_channel_properties),
- storvsc_on_channel_callback, device);
+ storvsc_on_channel_callback, device->channel);
if (ret != 0)
return ret;
@@ -1207,6 +1323,7 @@ static int storvsc_do_io(struct hv_device *device,
{
struct storvsc_device *stor_device;
struct vstor_packet *vstor_packet;
+ struct vmbus_channel *outgoing_channel;
int ret = 0;
vstor_packet = &request->vstor_packet;
@@ -1217,6 +1334,11 @@ static int storvsc_do_io(struct hv_device *device,
request->device = device;
+ /*
+ * Select an an appropriate channel to send the request out.
+ */
+
+ outgoing_channel = vmbus_get_outgoing_channel(device->channel);
vstor_packet->flags |= REQUEST_COMPLETION_FLAG;
@@ -1234,7 +1356,7 @@ static int storvsc_do_io(struct hv_device *device,
vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB;
if (request->data_buffer.len) {
- ret = vmbus_sendpacket_multipagebuffer(device->channel,
+ ret = vmbus_sendpacket_multipagebuffer(outgoing_channel,
&request->data_buffer,
vstor_packet,
(sizeof(struct vstor_packet) -
@@ -1580,6 +1702,7 @@ static struct scsi_host_template scsi_driver = {
enum {
SCSI_GUID,
IDE_GUID,
+ SFC_GUID,
};
static const struct hv_vmbus_device_id id_table[] = {
@@ -1591,6 +1714,11 @@ static const struct hv_vmbus_device_id id_table[] = {
{ HV_IDE_GUID,
.driver_data = IDE_GUID
},
+ /* Fibre Channel GUID */
+ {
+ HV_SYNTHFC_GUID,
+ .driver_data = SFC_GUID
+ },
{ },
};
@@ -1643,6 +1771,7 @@ static int storvsc_probe(struct hv_device *device,
}
stor_device->destroy = false;
+ stor_device->open_sub_channel = false;
init_waitqueue_head(&stor_device->waiting_to_drain);
stor_device->device = device;
stor_device->host = host;