win-pvdrivers

view xenvbd_scsiport/xenvbd.c @ 1061:df3ee58e8b4f

scsiport queue will freeze if something returns an error. Queue a timer to ensure it keeps moving.
author James Harper <james.harper@bendigoit.com.au>
date Tue Oct 01 18:46:50 2013 +1000 (2013-10-01)
parents ca993d6b5539
children 494bfa9c34c4
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
3 Copyright (C) 2007 James Harper
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
20 #define INITGUID
22 #include "xenvbd.h"
24 #pragma warning(disable: 4127)
26 /* Not really necessary but keeps PREfast happy */
27 DRIVER_INITIALIZE DriverEntry;
28 static IO_WORKITEM_ROUTINE XenVbd_DisconnectWorkItem;
30 static VOID XenVbd_ProcessSrbList(PXENVBD_DEVICE_DATA xvdd);
31 static BOOLEAN XenVbd_ResetBus(PXENVBD_DEVICE_DATA xvdd, ULONG PathId);
32 static VOID XenVbd_CompleteDisconnect(PXENVBD_DEVICE_DATA xvdd);
34 static BOOLEAN dump_mode = FALSE;
35 #define DUMP_MODE_ERROR_LIMIT 64
36 static ULONG dump_mode_errors = 0;
38 #define StorPortAcquireSpinLock(...) {}
39 #define StorPortReleaseSpinLock(...) {}
41 static ULONG
42 SxxxPortGetSystemAddress(PVOID device_extension, PSCSI_REQUEST_BLOCK srb, PVOID *system_address) {
43 UNREFERENCED_PARAMETER(device_extension);
44 *system_address = (PUCHAR)srb->DataBuffer;
45 return STATUS_SUCCESS;
46 }
48 static PHYSICAL_ADDRESS
49 SxxxPortGetPhysicalAddress(PVOID device_extension, PSCSI_REQUEST_BLOCK srb, PVOID virtual_address, ULONG *length) {
50 UNREFERENCED_PARAMETER(device_extension);
51 UNREFERENCED_PARAMETER(srb);
52 UNREFERENCED_PARAMETER(length);
53 return MmGetPhysicalAddress(virtual_address);
54 }
56 #define SxxxPortNotification(NotificationType, DeviceExtension, ...) XenVbd_Notification##NotificationType(DeviceExtension, __VA_ARGS__)
58 static VOID
59 XenVbd_NotificationRequestComplete(PXENVBD_DEVICE_DATA xvdd, PSCSI_REQUEST_BLOCK srb) {
60 PXENVBD_SCSIPORT_DATA xvsd = (PXENVBD_SCSIPORT_DATA)xvdd->xvsd;
61 srb_list_entry_t *srb_entry = srb->SrbExtension;
62 if (srb_entry->outstanding_requests != 0) {
63 FUNCTION_MSG("srb outstanding_requests = %d\n", srb_entry->outstanding_requests);
64 }
65 xvsd->outstanding--;
66 ScsiPortNotification(RequestComplete, xvsd, srb);
67 }
69 VOID
70 XenVbd_NotificationNextLuRequest(PXENVBD_DEVICE_DATA xvdd, UCHAR PathId, UCHAR TargetId, UCHAR Lun) {
71 ScsiPortNotification(NextLuRequest, xvdd->xvsd, PathId, TargetId, Lun);
72 }
74 VOID
75 XenVbd_NotificationNextRequest(PXENVBD_DEVICE_DATA xvdd) {
76 ScsiPortNotification(NextRequest, xvdd->xvsd);
77 }
80 VOID
81 XenVbd_NotificationBusChangeDetected(PXENVBD_DEVICE_DATA xvdd, UCHAR PathId) {
82 ScsiPortNotification(BusChangeDetected, xvdd->xvsd, PathId);
83 }
85 #include "..\xenvbd_common\common_miniport.h"
88 /* called in non-dump mode */
89 static ULONG
90 XenVbd_HwScsiFindAdapter(PVOID DeviceExtension, PVOID HwContext, PVOID BusInformation, PCHAR ArgumentString, PPORT_CONFIGURATION_INFORMATION ConfigInfo, PBOOLEAN Again) {
91 PXENVBD_SCSIPORT_DATA xvsd = (PXENVBD_SCSIPORT_DATA)DeviceExtension;
92 PXENVBD_DEVICE_DATA xvdd;
93 PACCESS_RANGE access_range;
95 UNREFERENCED_PARAMETER(HwContext);
96 UNREFERENCED_PARAMETER(BusInformation);
97 UNREFERENCED_PARAMETER(ArgumentString);
99 FUNCTION_ENTER();
100 FUNCTION_MSG("IRQL = %d\n", KeGetCurrentIrql());
101 FUNCTION_MSG("xvsd = %p\n", xvsd);
103 if (ConfigInfo->NumberOfAccessRanges != 1) {
104 FUNCTION_MSG("NumberOfAccessRanges wrong\n");
105 FUNCTION_EXIT();
106 return SP_RETURN_BAD_CONFIG;
107 }
108 if (XnGetVersion() != 1) {
109 FUNCTION_MSG("Wrong XnGetVersion\n");
110 FUNCTION_EXIT();
111 return SP_RETURN_BAD_CONFIG;
112 }
113 RtlZeroMemory(xvsd, FIELD_OFFSET(XENVBD_SCSIPORT_DATA, aligned_buffer_data));
115 access_range = &((*(ConfigInfo->AccessRanges))[0]);
117 if (!dump_mode) {
118 xvdd = (PXENVBD_DEVICE_DATA)(ULONG_PTR)access_range->RangeStart.QuadPart;
119 xvsd->xvdd = xvdd;
120 xvdd->xvsd = xvsd;
121 xvdd->aligned_buffer = (PVOID)((ULONG_PTR)((PUCHAR)xvsd->aligned_buffer_data + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
122 } else {
123 /* make a copy of xvdd and use that copy */
124 xvdd = (PXENVBD_DEVICE_DATA)xvsd->aligned_buffer_data;
125 memcpy(xvdd, (PVOID)(ULONG_PTR)access_range->RangeStart.QuadPart, sizeof(XENVBD_DEVICE_DATA));
126 /* make sure original xvdd is set to DISCONNECTED or resume will not work */
127 ((PXENVBD_DEVICE_DATA)(ULONG_PTR)access_range->RangeStart.QuadPart)->device_state = DEVICE_STATE_DISCONNECTED;
128 xvsd->xvdd = xvdd;
129 xvdd->xvsd = xvsd;
130 xvdd->aligned_buffer = (PVOID)((ULONG_PTR)((PUCHAR)xvsd->aligned_buffer_data + sizeof(XENVBD_DEVICE_DATA) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
131 if (xvsd->xvdd->device_state != DEVICE_STATE_ACTIVE) {
132 /* if we are not connected to the ring when we start dump mode then there is nothing we can do */
133 FUNCTION_MSG("Cannot connect backend in dump mode - state = %d\n", xvsd->xvdd->device_state);
134 return SP_RETURN_ERROR;
135 }
136 }
137 FUNCTION_MSG("aligned_buffer_data = %p\n", xvsd->aligned_buffer_data);
138 FUNCTION_MSG("aligned_buffer = %p\n", xvdd->aligned_buffer);
140 InitializeListHead(&xvdd->srb_list);
141 xvdd->aligned_buffer_in_use = FALSE;
142 /* align the buffer to PAGE_SIZE */
144 ConfigInfo->MaximumTransferLength = 4 * 1024 * 1024; //BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE;
145 ConfigInfo->NumberOfPhysicalBreaks = ConfigInfo->MaximumTransferLength >> PAGE_SHIFT; //BLKIF_MAX_SEGMENTS_PER_REQUEST - 1;
146 FUNCTION_MSG("ConfigInfo->MaximumTransferLength = %d\n", ConfigInfo->MaximumTransferLength);
147 FUNCTION_MSG("ConfigInfo->NumberOfPhysicalBreaks = %d\n", ConfigInfo->NumberOfPhysicalBreaks);
148 if (!dump_mode) {
149 xvdd->aligned_buffer_size = BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE;
150 } else {
151 xvdd->aligned_buffer_size = DUMP_MODE_UNALIGNED_PAGES * PAGE_SIZE;
152 }
154 FUNCTION_MSG("MultipleRequestPerLu = %d\n", ConfigInfo->MultipleRequestPerLu);
155 FUNCTION_MSG("TaggedQueuing = %d\n", ConfigInfo->TaggedQueuing);
156 FUNCTION_MSG("AutoRequestSense = %d\n", ConfigInfo->AutoRequestSense);
157 ConfigInfo->CachesData = FALSE;
158 ConfigInfo->MapBuffers = TRUE;
159 ConfigInfo->AlignmentMask = 0;
160 ConfigInfo->NumberOfBuses = 1;
161 ConfigInfo->InitiatorBusId[0] = 1;
162 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
163 ConfigInfo->MaximumNumberOfTargets = 2;
164 FUNCTION_MSG("MapBuffers = %d\n", ConfigInfo->MapBuffers);
165 FUNCTION_MSG("NeedPhysicalAddresses = %d\n", ConfigInfo->NeedPhysicalAddresses);
166 if (ConfigInfo->Dma64BitAddresses == SCSI_DMA64_SYSTEM_SUPPORTED) {
167 FUNCTION_MSG("Dma64BitAddresses supported\n");
168 ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_SUPPORTED;
169 ConfigInfo->ScatterGather = TRUE;
170 ConfigInfo->Master = TRUE;
171 } else {
172 FUNCTION_MSG("Dma64BitAddresses not supported\n");
173 ConfigInfo->ScatterGather = FALSE;
174 ConfigInfo->Master = FALSE;
175 }
176 *Again = FALSE;
178 FUNCTION_EXIT();
180 return SP_RETURN_FOUND;
181 }
183 /* Called at PASSIVE_LEVEL for non-dump mode */
184 static BOOLEAN
185 XenVbd_HwScsiInitialize(PVOID DeviceExtension) {
186 PXENVBD_SCSIPORT_DATA xvsd = (PXENVBD_SCSIPORT_DATA)DeviceExtension;
187 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)xvsd->xvdd;
188 ULONG i;
190 FUNCTION_ENTER();
191 FUNCTION_MSG("IRQL = %d\n", KeGetCurrentIrql());
192 FUNCTION_MSG("dump_mode = %d\n", dump_mode);
194 xvdd->shadow_free = 0;
195 memset(xvdd->shadows, 0, sizeof(blkif_shadow_t) * SHADOW_ENTRIES);
196 for (i = 0; i < SHADOW_ENTRIES; i++) {
197 xvdd->shadows[i].req.id = i;
198 /* make sure leftover real requests's are never confused with dump mode requests */
199 if (dump_mode)
200 xvdd->shadows[i].req.id |= SHADOW_ID_DUMP_FLAG;
201 put_shadow_on_freelist(xvdd, &xvdd->shadows[i]);
202 }
204 if (!dump_mode) {
205 /* nothing */
206 } else {
207 xvdd->grant_tag = (ULONG)'DUMP';
208 }
210 FUNCTION_EXIT();
212 return TRUE;
213 }
215 /* this is only used during hiber and dump */
216 static BOOLEAN
217 XenVbd_HwScsiInterrupt(PVOID DeviceExtension)
218 {
219 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
220 XenVbd_HandleEvent(xvsd->xvdd);
221 //SxxxPortNotification(NextLuRequest, xvdd, 0, 0, 0);
222 return TRUE;
223 }
225 static BOOLEAN
226 XenVbd_HwScsiResetBus(PVOID DeviceExtension, ULONG PathId)
227 {
228 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
229 return XenVbd_ResetBus(xvsd->xvdd, PathId);
230 }
232 static VOID
233 XenVbd_CompleteDisconnect(PXENVBD_DEVICE_DATA xvdd) {
234 PXENVBD_SCSIPORT_DATA xvsd = (PXENVBD_SCSIPORT_DATA)xvdd->xvsd;
235 PSCSI_REQUEST_BLOCK srb;
237 if (xvsd->stop_srb) {
238 srb = xvsd->stop_srb;
239 xvsd->stop_srb = NULL;
240 ScsiPortNotification(RequestComplete, xvsd, srb);
241 }
242 }
244 static VOID
245 XenVbd_HwScsiTimer(PVOID DeviceExtension) {
246 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
247 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)xvsd->xvdd;
249 //FUNCTION_MSG("HwScsiTimer\n");
250 XenVbd_HandleEvent(xvdd);
251 if (xvsd->outstanding) {
252 ScsiPortNotification(RequestTimerCall, xvsd, XenVbd_HwScsiTimer, 100000);
253 } else {
254 ScsiPortNotification(RequestTimerCall, xvsd, XenVbd_HwScsiTimer, 0);
255 }
256 }
258 static BOOLEAN
259 XenVbd_HwScsiStartIo(PVOID DeviceExtension, PSCSI_REQUEST_BLOCK srb) {
260 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
261 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)xvsd->xvdd;
262 PSRB_IO_CONTROL sic;
264 if ((LONG)xvsd->outstanding < 0) {
265 FUNCTION_MSG("HwScsiStartIo outstanding = %d\n", xvsd->outstanding);
266 }
267 if (srb->PathId != 0 || srb->TargetId != 0 || srb->Lun != 0) {
268 FUNCTION_MSG("HwScsiStartIo (Out of bounds - PathId = %d, TargetId = %d, Lun = %d)\n", srb->PathId, srb->TargetId, srb->Lun);
269 srb->SrbStatus = SRB_STATUS_NO_DEVICE;
270 ScsiPortNotification(RequestComplete, xvsd, srb);
271 } else if (srb->Function == SRB_FUNCTION_IO_CONTROL && memcmp(((PSRB_IO_CONTROL)srb->DataBuffer)->Signature, XENVBD_CONTROL_SIG, 8) == 0) {
272 sic = srb->DataBuffer;
273 switch(sic->ControlCode) {
274 case XENVBD_CONTROL_EVENT:
275 srb->SrbStatus = SRB_STATUS_SUCCESS;
276 ScsiPortNotification(RequestComplete, xvsd, srb);
277 break;
278 case XENVBD_CONTROL_STOP:
279 if (xvdd->shadow_free == SHADOW_ENTRIES) {
280 srb->SrbStatus = SRB_STATUS_SUCCESS;
281 ScsiPortNotification(RequestComplete, xvsd, srb);
282 FUNCTION_MSG("CONTROL_STOP done\n");
283 } else {
284 xvsd->stop_srb = srb;
285 FUNCTION_MSG("CONTROL_STOP pended\n");
286 }
287 break;
288 case XENVBD_CONTROL_START:
289 // we might need to reload a few things here...
290 ScsiPortNotification(RequestComplete, xvsd, srb);
291 break;
292 default:
293 FUNCTION_MSG("XENVBD_CONTROL_%d\n", sic->ControlCode);
294 srb->SrbStatus = SRB_STATUS_ERROR;
295 ScsiPortNotification(RequestComplete, xvsd, srb);
296 break;
297 }
298 } else if (xvdd->device_state == DEVICE_STATE_INACTIVE) {
299 FUNCTION_MSG("HwScsiStartIo Inactive Device (in StartIo)\n");
300 srb->SrbStatus = SRB_STATUS_NO_DEVICE;
301 ScsiPortNotification(RequestComplete, xvsd, srb);
302 } else {
303 xvsd->outstanding++;
304 XenVbd_PutSrbOnList(xvdd, srb);
305 }
306 /* HandleEvent also puts queued SRB's on the ring */
307 XenVbd_HandleEvent(xvdd);
308 /* need 2 spare slots - 1 for EVENT and 1 for STOP/START */
309 if (xvsd->outstanding < 30) {
310 ScsiPortNotification(NextLuRequest, xvsd, 0, 0, 0);
311 } else {
312 ScsiPortNotification(NextRequest, xvsd);
313 }
314 /* if there was an error returned by an SRB then the queue will freeze. Queue a timer to resolve this */
315 if (xvsd->outstanding) {
316 ScsiPortNotification(RequestTimerCall, xvsd, XenVbd_HwScsiTimer, 100000);
317 } else {
318 ScsiPortNotification(RequestTimerCall, xvsd, XenVbd_HwScsiTimer, 0);
319 }
320 return TRUE;
321 }
323 static SCSI_ADAPTER_CONTROL_STATUS
324 XenVbd_HwScsiAdapterControl(PVOID DeviceExtension, SCSI_ADAPTER_CONTROL_TYPE ControlType, PVOID Parameters) {
325 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
326 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)xvsd->xvdd;
327 SCSI_ADAPTER_CONTROL_STATUS Status = ScsiAdapterControlSuccess;
328 PSCSI_SUPPORTED_CONTROL_TYPE_LIST SupportedControlTypeList;
330 FUNCTION_ENTER();
331 FUNCTION_MSG("IRQL = %d\n", KeGetCurrentIrql());
332 FUNCTION_MSG("xvsd = %p\n", xvsd);
334 switch (ControlType) {
335 case ScsiQuerySupportedControlTypes:
336 SupportedControlTypeList = (PSCSI_SUPPORTED_CONTROL_TYPE_LIST)Parameters;
337 FUNCTION_MSG("ScsiQuerySupportedControlTypes (Max = %d)\n", SupportedControlTypeList->MaxControlType);
338 SupportedControlTypeList->SupportedTypeList[ScsiQuerySupportedControlTypes] = TRUE;
339 SupportedControlTypeList->SupportedTypeList[ScsiStopAdapter] = TRUE;
340 SupportedControlTypeList->SupportedTypeList[ScsiRestartAdapter] = TRUE;
341 break;
342 case ScsiStopAdapter:
343 FUNCTION_MSG("ScsiStopAdapter\n");
344 if (xvdd->device_state == DEVICE_STATE_INACTIVE) {
345 FUNCTION_MSG("inactive - nothing to do\n");
346 break;
347 }
348 XN_ASSERT(IsListEmpty(&xvdd->srb_list));
349 XN_ASSERT(xvdd->shadow_free == SHADOW_ENTRIES);
350 break;
351 case ScsiRestartAdapter:
352 FUNCTION_MSG("ScsiRestartAdapter\n");
353 if (xvdd->device_state == DEVICE_STATE_INACTIVE) {
354 FUNCTION_MSG("inactive - nothing to do\n");
355 break;
356 }
357 /* increase the tag every time we stop/start to track where the gref's came from */
358 xvdd->grant_tag++;
359 break;
360 case ScsiSetBootConfig:
361 FUNCTION_MSG("ScsiSetBootConfig\n");
362 break;
363 case ScsiSetRunningConfig:
364 FUNCTION_MSG("ScsiSetRunningConfig\n");
365 break;
366 default:
367 FUNCTION_MSG("UNKNOWN\n");
368 break;
369 }
371 FUNCTION_EXIT();
373 return Status;
374 }
376 NTSTATUS
377 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
378 ULONG status;
379 HW_INITIALIZATION_DATA HwInitializationData;
381 /* RegistryPath == NULL when we are invoked as a crash dump driver */
382 if (!RegistryPath) {
383 dump_mode = TRUE;
384 XnPrintDump();
385 }
387 FUNCTION_ENTER();
388 FUNCTION_MSG("IRQL = %d\n", KeGetCurrentIrql());
389 FUNCTION_MSG("DriverObject = %p, RegistryPath = %p\n", DriverObject, RegistryPath);
391 RtlZeroMemory(&HwInitializationData, sizeof(HW_INITIALIZATION_DATA));
392 HwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
393 HwInitializationData.AdapterInterfaceType = PNPBus; /* not Internal */
394 HwInitializationData.SrbExtensionSize = sizeof(srb_list_entry_t);
395 HwInitializationData.NumberOfAccessRanges = 1;
396 HwInitializationData.MapBuffers = TRUE;
397 HwInitializationData.NeedPhysicalAddresses = FALSE;
398 HwInitializationData.TaggedQueuing = TRUE;
399 HwInitializationData.AutoRequestSense = TRUE;
400 HwInitializationData.MultipleRequestPerLu = TRUE;
401 HwInitializationData.ReceiveEvent = FALSE;
402 HwInitializationData.HwInitialize = XenVbd_HwScsiInitialize;
403 HwInitializationData.HwStartIo = XenVbd_HwScsiStartIo;
404 HwInitializationData.HwFindAdapter = XenVbd_HwScsiFindAdapter;
405 HwInitializationData.HwResetBus = XenVbd_HwScsiResetBus;
406 HwInitializationData.HwAdapterControl = XenVbd_HwScsiAdapterControl;
407 if (!dump_mode) {
408 HwInitializationData.DeviceExtensionSize = FIELD_OFFSET(XENVBD_SCSIPORT_DATA, aligned_buffer_data) + UNALIGNED_BUFFER_DATA_SIZE;
409 } else {
410 HwInitializationData.HwInterrupt = XenVbd_HwScsiInterrupt;
411 HwInitializationData.DeviceExtensionSize = FIELD_OFFSET(XENVBD_SCSIPORT_DATA, aligned_buffer_data) + sizeof(XENVBD_DEVICE_DATA) + UNALIGNED_BUFFER_DATA_SIZE_DUMP_MODE;
412 }
413 status = ScsiPortInitialize(DriverObject, RegistryPath, &HwInitializationData, NULL);
415 if(!NT_SUCCESS(status)) {
416 FUNCTION_MSG("ScsiPortInitialize failed with status 0x%08x\n", status);
417 }
419 FUNCTION_EXIT();
421 return status;
422 }