diff options
Diffstat (limited to 'drivers/scsi/storvsc_drv.c')
| -rw-r--r-- | drivers/scsi/storvsc_drv.c | 506 |
1 files changed, 418 insertions, 88 deletions
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 528d52beaa1..9969fa1ef7c 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -55,10 +55,15 @@ * V1 RC < 2008/1/31: 1.0 * V1 RC > 2008/1/31: 2.0 * Win7: 4.2 + * Win8: 5.1 */ -#define VMSTOR_CURRENT_MAJOR 4 -#define VMSTOR_CURRENT_MINOR 2 + +#define VMSTOR_WIN7_MAJOR 4 +#define VMSTOR_WIN7_MINOR 2 + +#define VMSTOR_WIN8_MAJOR 5 +#define VMSTOR_WIN8_MINOR 1 /* Packet structure describing virtual storage requests. */ @@ -74,18 +79,103 @@ enum vstor_packet_operation { VSTOR_OPERATION_QUERY_PROTOCOL_VERSION = 9, VSTOR_OPERATION_QUERY_PROPERTIES = 10, VSTOR_OPERATION_ENUMERATE_BUS = 11, - VSTOR_OPERATION_MAXIMUM = 11 + VSTOR_OPERATION_FCHBA_DATA = 12, + VSTOR_OPERATION_CREATE_SUB_CHANNELS = 13, + VSTOR_OPERATION_MAXIMUM = 13 +}; + +/* + * WWN packet for Fibre Channel HBA + */ + +struct hv_fc_wwn_packet { + bool primary_active; + u8 reserved1; + u8 reserved2; + u8 primary_port_wwn[8]; + u8 primary_node_wwn[8]; + u8 secondary_port_wwn[8]; + u8 secondary_node_wwn[8]; }; + + +/* + * SRB Flag Bits + */ + +#define SRB_FLAGS_QUEUE_ACTION_ENABLE 0x00000002 +#define SRB_FLAGS_DISABLE_DISCONNECT 0x00000004 +#define SRB_FLAGS_DISABLE_SYNCH_TRANSFER 0x00000008 +#define SRB_FLAGS_BYPASS_FROZEN_QUEUE 0x00000010 +#define SRB_FLAGS_DISABLE_AUTOSENSE 0x00000020 +#define SRB_FLAGS_DATA_IN 0x00000040 +#define SRB_FLAGS_DATA_OUT 0x00000080 +#define SRB_FLAGS_NO_DATA_TRANSFER 0x00000000 +#define SRB_FLAGS_UNSPECIFIED_DIRECTION (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT) +#define SRB_FLAGS_NO_QUEUE_FREEZE 0x00000100 +#define SRB_FLAGS_ADAPTER_CACHE_ENABLE 0x00000200 +#define SRB_FLAGS_FREE_SENSE_BUFFER 0x00000400 + +/* + * This flag indicates the request is part of the workflow for processing a D3. + */ +#define SRB_FLAGS_D3_PROCESSING 0x00000800 +#define SRB_FLAGS_IS_ACTIVE 0x00010000 +#define SRB_FLAGS_ALLOCATED_FROM_ZONE 0x00020000 +#define SRB_FLAGS_SGLIST_FROM_POOL 0x00040000 +#define SRB_FLAGS_BYPASS_LOCKED_QUEUE 0x00080000 +#define SRB_FLAGS_NO_KEEP_AWAKE 0x00100000 +#define SRB_FLAGS_PORT_DRIVER_ALLOCSENSE 0x00200000 +#define SRB_FLAGS_PORT_DRIVER_SENSEHASPORT 0x00400000 +#define SRB_FLAGS_DONT_START_NEXT_PACKET 0x00800000 +#define SRB_FLAGS_PORT_DRIVER_RESERVED 0x0F000000 +#define SRB_FLAGS_CLASS_DRIVER_RESERVED 0xF0000000 + + /* * Platform neutral description of a scsi request - * this remains the same across the write regardless of 32/64 bit * note: it's patterned off the SCSI_PASS_THROUGH structure */ #define STORVSC_MAX_CMD_LEN 0x10 -#define STORVSC_SENSE_BUFFER_SIZE 0x12 + +#define POST_WIN7_STORVSC_SENSE_BUFFER_SIZE 0x14 +#define PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE 0x12 + +#define STORVSC_SENSE_BUFFER_SIZE 0x14 #define STORVSC_MAX_BUF_LEN_WITH_PADDING 0x14 +/* + * Sense buffer size changed in win8; have a run-time + * variable to track the size we should use. + */ +static int sense_buffer_size; + +/* + * The size of the vmscsi_request has changed in win8. The + * additional size is because of new elements added to the + * structure. These elements are valid only when we are talking + * to a win8 host. + * Track the correction to size we need to apply. + */ + +static int vmscsi_size_delta; +static int vmstor_current_major; +static int vmstor_current_minor; + +struct vmscsi_win8_extension { + /* + * The following were added in Windows 8 + */ + u16 reserve; + u8 queue_tag; + u8 queue_action; + u32 srb_flags; + u32 time_out_value; + u32 queue_sort_ey; +} __packed; + struct vmscsi_request { u16 length; u8 srb_status; @@ -108,6 +198,11 @@ struct vmscsi_request { u8 sense_data[STORVSC_SENSE_BUFFER_SIZE]; u8 reserved_array[STORVSC_MAX_BUF_LEN_WITH_PADDING]; }; + /* + * The following was added in win8. + */ + struct vmscsi_win8_extension win8_extension; + } __attribute((packed)); @@ -115,22 +210,18 @@ struct vmscsi_request { * This structure is sent during the intialization phase to get the different * properties of the channel. */ + +#define STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL 0x1 + struct vmstorage_channel_properties { - u16 protocol_version; - u8 path_id; - u8 target_id; + u32 reserved; + u16 max_channel_cnt; + u16 reserved1; - /* Note: port number is only really known on the client side */ - u32 port_number; - u32 flags; + u32 flags; u32 max_transfer_bytes; - /* - * This id is unique for each channel and will correspond with - * vendor specific data in the inquiry data. - */ - - u64 unique_id; + u64 reserved2; } __packed; /* This structure is sent during the storage protocol negotiations. */ @@ -175,6 +266,15 @@ struct vstor_packet { /* Used during version negotiations. */ struct vmstorage_protocol_version version; + + /* Fibre channel address packet */ + struct hv_fc_wwn_packet wwn_packet; + + /* Number of sub-channels to create */ + u16 sub_channel_count; + + /* This will be the maximum of the union members */ + u8 buffer[0x34]; }; } __packed; @@ -201,6 +301,7 @@ enum storvsc_request_type { #define SRB_STATUS_AUTOSENSE_VALID 0x80 #define SRB_STATUS_INVALID_LUN 0x20 #define SRB_STATUS_SUCCESS 0x01 +#define SRB_STATUS_ABORTED 0x02 #define SRB_STATUS_ERROR 0x04 /* @@ -220,7 +321,14 @@ static int storvsc_ringbuffer_size = (20 * PAGE_SIZE); module_param(storvsc_ringbuffer_size, int, S_IRUGO); MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); -#define STORVSC_MAX_IO_REQUESTS 128 +/* + * Timeout in seconds for all devices managed by this driver. + */ +static int storvsc_timeout = 180; + +#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 @@ -258,6 +366,7 @@ struct storvsc_device { bool destroy; bool drain_notify; + bool open_sub_channel; atomic_t num_outstanding_req; struct Scsi_Host *host; @@ -295,6 +404,25 @@ struct storvsc_scan_work { uint lun; }; +static void storvsc_device_scan(struct work_struct *work) +{ + struct storvsc_scan_work *wrk; + uint lun; + struct scsi_device *sdev; + + wrk = container_of(work, struct storvsc_scan_work, work); + lun = wrk->lun; + + sdev = scsi_device_lookup(wrk->host, 0, 0, lun); + if (!sdev) + goto done; + scsi_rescan_device(&sdev->sdev_gendev); + scsi_device_put(sdev); + +done: + kfree(wrk); +} + static void storvsc_bus_scan(struct work_struct *work) { struct storvsc_scan_work *wrk; @@ -467,6 +595,7 @@ static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, if (!bounce_sgl) return NULL; + sg_init_table(bounce_sgl, num_pages); for (i = 0; i < num_pages; i++) { page_buf = alloc_page(GFP_ATOMIC); if (!page_buf) @@ -629,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) @@ -653,7 +874,8 @@ static int storvsc_channel_init(struct hv_device *device) vstor_packet->flags = REQUEST_COMPLETION_FLAG; ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), + (sizeof(struct vstor_packet) - + vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -677,7 +899,7 @@ static int storvsc_channel_init(struct hv_device *device) vstor_packet->flags = REQUEST_COMPLETION_FLAG; vstor_packet->version.major_minor = - storvsc_get_version(VMSTOR_CURRENT_MAJOR, VMSTOR_CURRENT_MINOR); + storvsc_get_version(vmstor_current_major, vmstor_current_minor); /* * The revision number is only used in Windows; set it to 0. @@ -685,7 +907,8 @@ static int storvsc_channel_init(struct hv_device *device) vstor_packet->version.revision = 0; ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), + (sizeof(struct vstor_packet) - + vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -706,11 +929,10 @@ static int storvsc_channel_init(struct hv_device *device) memset(vstor_packet, 0, sizeof(struct vstor_packet)); vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES; vstor_packet->flags = REQUEST_COMPLETION_FLAG; - vstor_packet->storage_channel_properties.port_number = - stor_device->port_number; ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), + (sizeof(struct vstor_packet) - + vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -728,16 +950,26 @@ static int storvsc_channel_init(struct hv_device *device) vstor_packet->status != 0) goto cleanup; - stor_device->path_id = vstor_packet->storage_channel_properties.path_id; - stor_device->target_id - = vstor_packet->storage_channel_properties.target_id; + /* + * 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; ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), + (sizeof(struct vstor_packet) - + vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -755,11 +987,74 @@ 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; } +static void storvsc_handle_error(struct vmscsi_request *vm_srb, + struct scsi_cmnd *scmnd, + struct Scsi_Host *host, + u8 asc, u8 ascq) +{ + struct storvsc_scan_work *wrk; + void (*process_err_fn)(struct work_struct *work); + bool do_work = false; + + switch (vm_srb->srb_status) { + case SRB_STATUS_ERROR: + /* + * If there is an error; offline the device since all + * error recovery strategies would have already been + * deployed on the host side. However, if the command + * were a pass-through command deal with it appropriately. + */ + switch (scmnd->cmnd[0]) { + case ATA_16: + case ATA_12: + set_host_byte(scmnd, DID_PASSTHROUGH); + break; + default: + set_host_byte(scmnd, DID_TARGET_FAILURE); + } + break; + case SRB_STATUS_INVALID_LUN: + do_work = true; + process_err_fn = storvsc_remove_lun; + break; + case (SRB_STATUS_ABORTED | SRB_STATUS_AUTOSENSE_VALID): + if ((asc == 0x2a) && (ascq == 0x9)) { + do_work = true; + process_err_fn = storvsc_device_scan; + /* + * Retry the I/O that trigerred this. + */ + set_host_byte(scmnd, DID_REQUEUE); + } + break; + } + + if (!do_work) + return; + + /* + * We need to schedule work to process this error; schedule it. + */ + wrk = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC); + if (!wrk) { + set_host_byte(scmnd, DID_TARGET_FAILURE); + return; + } + + wrk->host = host; + wrk->lun = vm_srb->lun; + INIT_WORK(&wrk->work, process_err_fn); + schedule_work(&wrk->work); +} + static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) { @@ -768,8 +1063,13 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) void (*scsi_done_fn)(struct scsi_cmnd *); struct scsi_sense_hdr sense_hdr; struct vmscsi_request *vm_srb; - struct storvsc_scan_work *wrk; struct stor_mem_pools *memp = scmnd->device->hostdata; + struct Scsi_Host *host; + struct storvsc_device *stor_dev; + struct hv_device *dev = host_dev->dev; + + stor_dev = get_in_stor_device(dev); + host = stor_dev->host; vm_srb = &cmd_request->vstor_packet.vm_srb; if (cmd_request->bounce_sgl_count) { @@ -782,55 +1082,18 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) cmd_request->bounce_sgl_count); } - /* - * If there is an error; offline the device since all - * error recovery strategies would have already been - * deployed on the host side. However, if the command - * were a pass-through command deal with it appropriately. - */ scmnd->result = vm_srb->scsi_status; - if (vm_srb->srb_status == SRB_STATUS_ERROR) { - switch (scmnd->cmnd[0]) { - case ATA_16: - case ATA_12: - set_host_byte(scmnd, DID_PASSTHROUGH); - break; - default: - set_host_byte(scmnd, DID_TARGET_FAILURE); - } - } - - - /* - * If the LUN is invalid; remove the device. - */ - if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) { - struct storvsc_device *stor_dev; - struct hv_device *dev = host_dev->dev; - struct Scsi_Host *host; - - stor_dev = get_in_stor_device(dev); - host = stor_dev->host; - - wrk = kmalloc(sizeof(struct storvsc_scan_work), - GFP_ATOMIC); - if (!wrk) { - scmnd->result = DID_TARGET_FAILURE << 16; - } else { - wrk->host = host; - wrk->lun = vm_srb->lun; - INIT_WORK(&wrk->work, storvsc_remove_lun); - schedule_work(&wrk->work); - } - } - if (scmnd->result) { if (scsi_normalize_sense(scmnd->sense_buffer, SCSI_SENSE_BUFFERSIZE, &sense_hdr)) scsi_print_sense_hdr("storvsc", &sense_hdr); } + if (vm_srb->srb_status != SRB_STATUS_SUCCESS) + storvsc_handle_error(vm_srb, scmnd, host, sense_hdr.asc, + sense_hdr.ascq); + scsi_set_resid(scmnd, cmd_request->data_buffer.len - vm_srb->data_transfer_length); @@ -948,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; @@ -956,14 +1220,19 @@ 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, - ALIGN(sizeof(struct vstor_packet), 8), + ret = vmbus_recvpacket(channel, packet, + ALIGN((sizeof(struct vstor_packet) - + vmscsi_size_delta), 8), &bytes_recvd, &request_id); if (ret == 0 && bytes_recvd > 0) { @@ -974,7 +1243,8 @@ static void storvsc_on_channel_callback(void *context) (request == &stor_device->reset_request)) { memcpy(&request->vstor_packet, packet, - sizeof(struct vstor_packet)); + (sizeof(struct vstor_packet) - + vmscsi_size_delta)); complete(&request->wait_event); } else { storvsc_on_receive(device, @@ -1001,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; @@ -1053,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; @@ -1063,14 +1334,20 @@ 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; - vstor_packet->vm_srb.length = sizeof(struct vmscsi_request); + vstor_packet->vm_srb.length = (sizeof(struct vmscsi_request) - + vmscsi_size_delta); - vstor_packet->vm_srb.sense_info_length = STORVSC_SENSE_BUFFER_SIZE; + vstor_packet->vm_srb.sense_info_length = sense_buffer_size; vstor_packet->vm_srb.data_transfer_length = @@ -1079,14 +1356,16 @@ 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), + (sizeof(struct vstor_packet) - + vmscsi_size_delta), (unsigned long)request); } else { ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), + (sizeof(struct vstor_packet) - + vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -1140,6 +1419,9 @@ static void storvsc_device_destroy(struct scsi_device *sdevice) { struct stor_mem_pools *memp = sdevice->hostdata; + if (!memp) + return; + mempool_destroy(memp->request_mempool); kmem_cache_destroy(memp->request_pool); kfree(memp); @@ -1155,6 +1437,10 @@ static int storvsc_device_configure(struct scsi_device *sdevice) blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); + blk_queue_rq_timeout(sdevice->request_queue, (storvsc_timeout * HZ)); + + sdevice->no_write_same = 1; + return 0; } @@ -1206,7 +1492,8 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) vstor_packet->vm_srb.path_id = stor_device->path_id; ret = vmbus_sendpacket(device->channel, vstor_packet, - sizeof(struct vstor_packet), + (sizeof(struct vstor_packet) - + vmscsi_size_delta), (unsigned long)&stor_device->reset_request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -1221,7 +1508,12 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) /* * At this point, all outstanding requests in the adapter * should have been flushed out and return to us + * There is a potential race here where the host may be in + * the process of responding when we return from here. + * Just wait for all in-transit packets to be accounted for + * before we return from here. */ + storvsc_wait_to_drain(stor_device); return SUCCESS; } @@ -1232,6 +1524,8 @@ static bool storvsc_scsi_cmd_ok(struct scsi_cmnd *scmnd) u8 scsi_op = scmnd->cmnd[0]; switch (scsi_op) { + /* the host does not handle WRITE_SAME, log accident usage */ + case WRITE_SAME: /* * smartd sends this command and the host does not handle * this. So, don't send it. @@ -1284,18 +1578,28 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) scmnd->host_scribble = (unsigned char *)cmd_request; vm_srb = &cmd_request->vstor_packet.vm_srb; + vm_srb->win8_extension.time_out_value = 60; /* 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; break; } @@ -1396,22 +1700,29 @@ static struct scsi_host_template scsi_driver = { .use_clustering = DISABLE_CLUSTERING, /* Make sure we dont get a sg segment crosses a page boundary */ .dma_boundary = PAGE_SIZE-1, + .no_write_same = 1, }; enum { SCSI_GUID, IDE_GUID, + SFC_GUID, }; static const struct hv_vmbus_device_id id_table[] = { /* SCSI guid */ - { VMBUS_DEVICE(0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, - 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f) - .driver_data = SCSI_GUID }, + { HV_SCSI_GUID, + .driver_data = SCSI_GUID + }, /* IDE guid */ - { VMBUS_DEVICE(0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, - 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5) - .driver_data = IDE_GUID }, + { HV_IDE_GUID, + .driver_data = IDE_GUID + }, + /* Fibre Channel GUID */ + { + HV_SYNTHFC_GUID, + .driver_data = SFC_GUID + }, { }, }; @@ -1427,6 +1738,24 @@ static int storvsc_probe(struct hv_device *device, int target = 0; struct storvsc_device *stor_device; + /* + * Based on the windows host we are running on, + * 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 { + 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; + } + + host = scsi_host_alloc(&scsi_driver, sizeof(struct hv_host_device)); if (!host) @@ -1446,6 +1775,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; @@ -1536,7 +1866,8 @@ static int __init storvsc_drv_init(void) max_outstanding_req_per_channel = ((storvsc_ringbuffer_size - PAGE_SIZE) / ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + - sizeof(struct vstor_packet) + sizeof(u64), + sizeof(struct vstor_packet) + sizeof(u64) - + vmscsi_size_delta, sizeof(u64))); if (max_outstanding_req_per_channel < @@ -1552,7 +1883,6 @@ static void __exit storvsc_drv_exit(void) } MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V virtual storage driver"); module_init(storvsc_drv_init); module_exit(storvsc_drv_exit); |
