diff options
Diffstat (limited to 'drivers/scsi/storvsc_drv.c')
-rw-r--r-- | drivers/scsi/storvsc_drv.c | 60 |
1 files changed, 37 insertions, 23 deletions
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 2e4fa77445fd..6bc5453cea8a 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -216,18 +216,6 @@ struct vmscsi_request { } __attribute((packed)); - -/* - * 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. This value - * will likely change during protocol negotiation but it is - * valid to start by assuming pre-Win8. - */ -static int vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); - /* * The list of storage protocols in order of preference. */ @@ -451,6 +439,17 @@ struct storvsc_device { unsigned char target_id; /* + * 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. This value + * will likely change during protocol negotiation but it is + * valid to start by assuming pre-Win8. + */ + int vmscsi_size_delta; + + /* * Max I/O, the device can support. */ u32 max_transfer_bytes; @@ -769,7 +768,7 @@ static void handle_multichannel_storage(struct hv_device *device, int max_chns) ret = vmbus_sendpacket(device->channel, vstor_packet, (sizeof(struct vstor_packet) - - vmscsi_size_delta), + stor_device->vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -823,9 +822,14 @@ static int storvsc_execute_vstor_op(struct hv_device *device, struct storvsc_cmd_request *request, bool status_check) { + struct storvsc_device *stor_device; struct vstor_packet *vstor_packet; int ret, t; + stor_device = get_out_stor_device(device); + if (!stor_device) + return -ENODEV; + vstor_packet = &request->vstor_packet; init_completion(&request->wait_event); @@ -833,7 +837,7 @@ static int storvsc_execute_vstor_op(struct hv_device *device, ret = vmbus_sendpacket(device->channel, vstor_packet, (sizeof(struct vstor_packet) - - vmscsi_size_delta), + stor_device->vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -910,7 +914,7 @@ static int storvsc_channel_init(struct hv_device *device, bool is_fc) sense_buffer_size = vmstor_protocols[i].sense_buffer_size; - vmscsi_size_delta = + stor_device->vmscsi_size_delta = vmstor_protocols[i].vmscsi_size_delta; break; @@ -1261,10 +1265,16 @@ static void storvsc_on_channel_callback(void *context) request = (struct storvsc_cmd_request *)(unsigned long)cmd_rqst; + if (hv_pkt_datalen(desc) < sizeof(struct vstor_packet) - + stor_device->vmscsi_size_delta) { + dev_err(&device->device, "Invalid packet len\n"); + continue; + } + if (request == &stor_device->init_request || request == &stor_device->reset_request) { memcpy(&request->vstor_packet, packet, - (sizeof(struct vstor_packet) - vmscsi_size_delta)); + (sizeof(struct vstor_packet) - stor_device->vmscsi_size_delta)); complete(&request->wait_event); } else { storvsc_on_receive(stor_device, packet, request); @@ -1482,7 +1492,7 @@ found_channel: vstor_packet->flags |= REQUEST_COMPLETION_FLAG; vstor_packet->vm_srb.length = (sizeof(struct vmscsi_request) - - vmscsi_size_delta); + stor_device->vmscsi_size_delta); vstor_packet->vm_srb.sense_info_length = sense_buffer_size; @@ -1499,12 +1509,12 @@ found_channel: request->payload, request->payload_sz, vstor_packet, (sizeof(struct vstor_packet) - - vmscsi_size_delta), + stor_device->vmscsi_size_delta), (unsigned long)request); } else { ret = vmbus_sendpacket(outgoing_channel, vstor_packet, (sizeof(struct vstor_packet) - - vmscsi_size_delta), + stor_device->vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -1609,7 +1619,7 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) ret = vmbus_sendpacket(device->channel, vstor_packet, (sizeof(struct vstor_packet) - - vmscsi_size_delta), + stor_device->vmscsi_size_delta), (unsigned long)&stor_device->reset_request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); @@ -1661,7 +1671,7 @@ static bool storvsc_scsi_cmd_ok(struct scsi_cmnd *scmnd) * this. So, don't send it. */ case SET_WINDOW: - scmnd->result = ILLEGAL_REQUEST << 16; + scmnd->result = DID_ERROR << 16; allowed = false; break; default: @@ -1959,6 +1969,7 @@ static int storvsc_probe(struct hv_device *device, init_waitqueue_head(&stor_device->waiting_to_drain); stor_device->device = device; stor_device->host = host; + stor_device->vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); spin_lock_init(&stor_device->lock); hv_set_drvdata(device, stor_device); @@ -2161,12 +2172,15 @@ static int __init storvsc_drv_init(void) * than the ring buffer size since that page is reserved for * the ring buffer indices) by the max request size (which is * vmbus_channel_packet_multipage_buffer + struct vstor_packet + u64) + * + * The computation underestimates max_outstanding_req_per_channel + * for Win7 and older hosts because it does not take into account + * the vmscsi_size_delta correction to the max request size. */ max_outstanding_req_per_channel = ((storvsc_ringbuffer_size - PAGE_SIZE) / ALIGN(MAX_MULTIPAGE_BUFFER_PACKET + - sizeof(struct vstor_packet) + sizeof(u64) - - vmscsi_size_delta, + sizeof(struct vstor_packet) + sizeof(u64), sizeof(u64))); #if IS_ENABLED(CONFIG_SCSI_FC_ATTRS) |