win-pvdrivers

view xenvbd_scsiport/xenvbd.c @ 1106:2d392ecdd366

Fix race is xenvbd causing 30 second freeze under high load
author James Harper <james.harper@bendigoit.com.au>
date Tue Nov 11 23:08:11 2014 +1100 (2014-11-11)
parents aa8ced6a86b0
children
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
4 Copyright (c) 2014, James Harper
5 All rights reserved.
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 * Neither the name of James Harper nor the
15 names of its contributors may be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL JAMES HARPER BE LIABLE FOR ANY
22 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
30 #define INITGUID
32 #include "xenvbd.h"
34 #pragma warning(disable: 4127)
36 /* Not really necessary but keeps PREfast happy */
37 DRIVER_INITIALIZE DriverEntry;
38 static IO_WORKITEM_ROUTINE XenVbd_DisconnectWorkItem;
40 static VOID XenVbd_ProcessSrbList(PXENVBD_DEVICE_DATA xvdd);
41 static BOOLEAN XenVbd_ResetBus(PXENVBD_DEVICE_DATA xvdd, ULONG PathId);
42 static VOID XenVbd_CompleteDisconnect(PXENVBD_DEVICE_DATA xvdd);
44 static BOOLEAN dump_mode = FALSE;
45 #define DUMP_MODE_ERROR_LIMIT 64
46 static ULONG dump_mode_errors = 0;
48 #define StorPortAcquireSpinLock(...) {}
49 #define StorPortReleaseSpinLock(...) {}
51 static ULONG
52 SxxxPortGetSystemAddress(PVOID device_extension, PSCSI_REQUEST_BLOCK srb, PVOID *system_address) {
53 UNREFERENCED_PARAMETER(device_extension);
54 *system_address = (PUCHAR)srb->DataBuffer;
55 return STATUS_SUCCESS;
56 }
58 static PHYSICAL_ADDRESS
59 SxxxPortGetPhysicalAddress(PVOID device_extension, PSCSI_REQUEST_BLOCK srb, PVOID virtual_address, ULONG *length) {
60 UNREFERENCED_PARAMETER(device_extension);
61 UNREFERENCED_PARAMETER(srb);
62 UNREFERENCED_PARAMETER(length);
63 return MmGetPhysicalAddress(virtual_address);
64 }
66 #define SxxxPortNotification(NotificationType, DeviceExtension, ...) XenVbd_Notification##NotificationType(DeviceExtension, __VA_ARGS__)
68 static VOID
69 XenVbd_NotificationRequestComplete(PXENVBD_DEVICE_DATA xvdd, PSCSI_REQUEST_BLOCK srb) {
70 PXENVBD_SCSIPORT_DATA xvsd = (PXENVBD_SCSIPORT_DATA)xvdd->xvsd;
71 srb_list_entry_t *srb_entry = srb->SrbExtension;
72 if (srb_entry->outstanding_requests != 0) {
73 FUNCTION_MSG("srb outstanding_requests = %d\n", srb_entry->outstanding_requests);
74 }
75 xvsd->outstanding--;
76 ScsiPortNotification(RequestComplete, xvsd, srb);
77 }
79 VOID
80 XenVbd_NotificationNextLuRequest(PXENVBD_DEVICE_DATA xvdd, UCHAR PathId, UCHAR TargetId, UCHAR Lun) {
81 ScsiPortNotification(NextLuRequest, xvdd->xvsd, PathId, TargetId, Lun);
82 }
84 VOID
85 XenVbd_NotificationNextRequest(PXENVBD_DEVICE_DATA xvdd) {
86 ScsiPortNotification(NextRequest, xvdd->xvsd);
87 }
90 VOID
91 XenVbd_NotificationBusChangeDetected(PXENVBD_DEVICE_DATA xvdd, UCHAR PathId) {
92 ScsiPortNotification(BusChangeDetected, xvdd->xvsd, PathId);
93 }
95 #include "..\xenvbd_common\common_miniport.h"
98 /* called in non-dump & dump mode */
99 static ULONG
100 XenVbd_HwScsiFindAdapter(PVOID DeviceExtension, PVOID HwContext, PVOID BusInformation, PCHAR ArgumentString, PPORT_CONFIGURATION_INFORMATION ConfigInfo, PBOOLEAN Again) {
101 PXENVBD_SCSIPORT_DATA xvsd = (PXENVBD_SCSIPORT_DATA)DeviceExtension;
102 PXENVBD_DEVICE_DATA xvdd;
103 PACCESS_RANGE access_range;
105 UNREFERENCED_PARAMETER(HwContext);
106 UNREFERENCED_PARAMETER(BusInformation);
107 UNREFERENCED_PARAMETER(ArgumentString);
109 FUNCTION_ENTER();
110 FUNCTION_MSG("IRQL = %d\n", KeGetCurrentIrql());
111 FUNCTION_MSG("xvsd = %p\n", xvsd);
113 if (ConfigInfo->NumberOfAccessRanges != 1) {
114 FUNCTION_MSG("NumberOfAccessRanges wrong\n");
115 FUNCTION_EXIT();
116 return SP_RETURN_BAD_CONFIG;
117 }
118 if (XnGetVersion() != 1) {
119 FUNCTION_MSG("Wrong XnGetVersion\n");
120 FUNCTION_EXIT();
121 return SP_RETURN_BAD_CONFIG;
122 }
123 RtlZeroMemory(xvsd, FIELD_OFFSET(XENVBD_SCSIPORT_DATA, aligned_buffer_data));
125 access_range = &((*(ConfigInfo->AccessRanges))[0]);
127 if (!dump_mode) {
128 xvdd = (PXENVBD_DEVICE_DATA)(ULONG_PTR)access_range->RangeStart.QuadPart;
129 xvsd->xvdd = xvdd;
130 xvdd->xvsd = xvsd;
131 xvdd->aligned_buffer = (PVOID)((ULONG_PTR)((PUCHAR)xvsd->aligned_buffer_data + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
132 /* save hypercall_stubs for crash dump */
133 xvsd->hypercall_stubs = XnGetHypercallStubs();
134 } else {
135 /* make a copy of xvdd and use that copy */
136 xvdd = (PXENVBD_DEVICE_DATA)xvsd->aligned_buffer_data;
137 memcpy(xvdd, (PVOID)(ULONG_PTR)access_range->RangeStart.QuadPart, sizeof(XENVBD_DEVICE_DATA));
138 /* make sure original xvdd is set to DISCONNECTED or resume will not work */
139 ((PXENVBD_DEVICE_DATA)(ULONG_PTR)access_range->RangeStart.QuadPart)->device_state = DEVICE_STATE_DISCONNECTED;
140 xvsd->xvdd = xvdd;
141 xvdd->xvsd = xvsd;
142 xvdd->aligned_buffer = (PVOID)((ULONG_PTR)((PUCHAR)xvsd->aligned_buffer_data + sizeof(XENVBD_DEVICE_DATA) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
143 /* restore hypercall_stubs into dump_xenpci */
144 XnSetHypercallStubs(xvsd->hypercall_stubs);
145 if (xvsd->xvdd->device_state != DEVICE_STATE_ACTIVE) {
146 /* if we are not connected to the ring when we start dump mode then there is nothing we can do */
147 FUNCTION_MSG("Cannot connect backend in dump mode - state = %d\n", xvsd->xvdd->device_state);
148 return SP_RETURN_ERROR;
149 }
150 }
151 FUNCTION_MSG("aligned_buffer_data = %p\n", xvsd->aligned_buffer_data);
152 FUNCTION_MSG("aligned_buffer = %p\n", xvdd->aligned_buffer);
154 InitializeListHead(&xvdd->srb_list);
155 xvdd->aligned_buffer_in_use = FALSE;
156 /* align the buffer to PAGE_SIZE */
158 ConfigInfo->MaximumTransferLength = 4 * 1024 * 1024; //BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE;
159 ConfigInfo->NumberOfPhysicalBreaks = ConfigInfo->MaximumTransferLength >> PAGE_SHIFT; //BLKIF_MAX_SEGMENTS_PER_REQUEST - 1;
160 FUNCTION_MSG("ConfigInfo->MaximumTransferLength = %d\n", ConfigInfo->MaximumTransferLength);
161 FUNCTION_MSG("ConfigInfo->NumberOfPhysicalBreaks = %d\n", ConfigInfo->NumberOfPhysicalBreaks);
162 if (!dump_mode) {
163 xvdd->aligned_buffer_size = BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE;
164 } else {
165 xvdd->aligned_buffer_size = DUMP_MODE_UNALIGNED_PAGES * PAGE_SIZE;
166 }
168 FUNCTION_MSG("MultipleRequestPerLu = %d\n", ConfigInfo->MultipleRequestPerLu);
169 FUNCTION_MSG("TaggedQueuing = %d\n", ConfigInfo->TaggedQueuing);
170 FUNCTION_MSG("AutoRequestSense = %d\n", ConfigInfo->AutoRequestSense);
171 ConfigInfo->CachesData = FALSE;
172 ConfigInfo->MapBuffers = TRUE;
173 ConfigInfo->AlignmentMask = 0;
174 ConfigInfo->NumberOfBuses = 1;
175 ConfigInfo->InitiatorBusId[0] = 1;
176 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
177 ConfigInfo->MaximumNumberOfTargets = 2;
178 FUNCTION_MSG("MapBuffers = %d\n", ConfigInfo->MapBuffers);
179 FUNCTION_MSG("NeedPhysicalAddresses = %d\n", ConfigInfo->NeedPhysicalAddresses);
180 if (ConfigInfo->Dma64BitAddresses == SCSI_DMA64_SYSTEM_SUPPORTED) {
181 FUNCTION_MSG("Dma64BitAddresses supported\n");
182 ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_SUPPORTED;
183 ConfigInfo->ScatterGather = TRUE;
184 ConfigInfo->Master = TRUE;
185 } else {
186 FUNCTION_MSG("Dma64BitAddresses not supported\n");
187 ConfigInfo->ScatterGather = FALSE;
188 ConfigInfo->Master = FALSE;
189 }
190 *Again = FALSE;
192 FUNCTION_EXIT();
194 return SP_RETURN_FOUND;
195 }
197 /* Called at PASSIVE_LEVEL for non-dump mode */
198 static BOOLEAN
199 XenVbd_HwScsiInitialize(PVOID DeviceExtension) {
200 PXENVBD_SCSIPORT_DATA xvsd = (PXENVBD_SCSIPORT_DATA)DeviceExtension;
201 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)xvsd->xvdd;
202 ULONG i;
204 FUNCTION_ENTER();
205 FUNCTION_MSG("IRQL = %d\n", KeGetCurrentIrql());
206 FUNCTION_MSG("dump_mode = %d\n", dump_mode);
208 xvdd->shadow_free = 0;
209 memset(xvdd->shadows, 0, sizeof(blkif_shadow_t) * SHADOW_ENTRIES);
210 for (i = 0; i < SHADOW_ENTRIES; i++) {
211 xvdd->shadows[i].req.id = i;
212 /* make sure leftover real requests's are never confused with dump mode requests */
213 if (dump_mode)
214 xvdd->shadows[i].req.id |= SHADOW_ID_DUMP_FLAG;
215 put_shadow_on_freelist(xvdd, &xvdd->shadows[i]);
216 }
218 if (!dump_mode) {
219 /* nothing */
220 } else {
221 xvdd->grant_tag = (ULONG)'DUMP';
222 }
224 FUNCTION_EXIT();
226 return TRUE;
227 }
229 /* this is only used during hiber and dump */
230 static BOOLEAN
231 XenVbd_HwScsiInterrupt(PVOID DeviceExtension)
232 {
233 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
234 XenVbd_HandleEvent(xvsd->xvdd);
235 //SxxxPortNotification(NextLuRequest, xvdd, 0, 0, 0);
236 return TRUE;
237 }
239 static BOOLEAN
240 XenVbd_HwScsiResetBus(PVOID DeviceExtension, ULONG PathId)
241 {
242 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
243 return XenVbd_ResetBus(xvsd->xvdd, PathId);
244 }
246 static VOID
247 XenVbd_CompleteDisconnect(PXENVBD_DEVICE_DATA xvdd) {
248 PXENVBD_SCSIPORT_DATA xvsd = (PXENVBD_SCSIPORT_DATA)xvdd->xvsd;
249 PSCSI_REQUEST_BLOCK srb;
251 if (xvsd->stop_srb) {
252 srb = xvsd->stop_srb;
253 xvsd->stop_srb = NULL;
254 ScsiPortNotification(RequestComplete, xvsd, srb);
255 }
256 }
258 static VOID
259 XenVbd_HwScsiTimer(PVOID DeviceExtension) {
260 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
261 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)xvsd->xvdd;
263 //FUNCTION_MSG("HwScsiTimer\n");
264 XenVbd_HandleEvent(xvdd);
265 if (xvsd->outstanding) {
266 ScsiPortNotification(RequestTimerCall, xvsd, XenVbd_HwScsiTimer, 100000);
267 } else {
268 ScsiPortNotification(RequestTimerCall, xvsd, XenVbd_HwScsiTimer, 0);
269 }
270 }
272 static BOOLEAN
273 XenVbd_HwScsiStartIo(PVOID DeviceExtension, PSCSI_REQUEST_BLOCK srb) {
274 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
275 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)xvsd->xvdd;
276 PSRB_IO_CONTROL sic;
278 if ((LONG)xvsd->outstanding < 0) {
279 FUNCTION_MSG("HwScsiStartIo outstanding = %d\n", xvsd->outstanding);
280 }
281 if (srb->PathId != 0 || srb->TargetId != 0 || srb->Lun != 0) {
282 FUNCTION_MSG("HwScsiStartIo (Out of bounds - PathId = %d, TargetId = %d, Lun = %d)\n", srb->PathId, srb->TargetId, srb->Lun);
283 srb->SrbStatus = SRB_STATUS_NO_DEVICE;
284 ScsiPortNotification(RequestComplete, xvsd, srb);
285 } else if (srb->Function == SRB_FUNCTION_IO_CONTROL && memcmp(((PSRB_IO_CONTROL)srb->DataBuffer)->Signature, XENVBD_CONTROL_SIG, 8) == 0) {
286 sic = srb->DataBuffer;
287 switch(sic->ControlCode) {
288 case XENVBD_CONTROL_EVENT:
289 srb->SrbStatus = SRB_STATUS_SUCCESS;
290 ScsiPortNotification(RequestComplete, xvsd, srb);
291 break;
292 case XENVBD_CONTROL_STOP:
293 if (xvdd->shadow_free == SHADOW_ENTRIES) {
294 srb->SrbStatus = SRB_STATUS_SUCCESS;
295 ScsiPortNotification(RequestComplete, xvsd, srb);
296 FUNCTION_MSG("CONTROL_STOP done\n");
297 } else {
298 xvsd->stop_srb = srb;
299 FUNCTION_MSG("CONTROL_STOP pended\n");
300 }
301 break;
302 case XENVBD_CONTROL_START:
303 // we might need to reload a few things here...
304 ScsiPortNotification(RequestComplete, xvsd, srb);
305 break;
306 default:
307 FUNCTION_MSG("XENVBD_CONTROL_%d\n", sic->ControlCode);
308 srb->SrbStatus = SRB_STATUS_ERROR;
309 ScsiPortNotification(RequestComplete, xvsd, srb);
310 break;
311 }
312 } else if (xvdd->device_state == DEVICE_STATE_INACTIVE) {
313 FUNCTION_MSG("HwScsiStartIo Inactive Device (in StartIo)\n");
314 srb->SrbStatus = SRB_STATUS_NO_DEVICE;
315 ScsiPortNotification(RequestComplete, xvsd, srb);
316 } else {
317 xvsd->outstanding++;
318 XenVbd_PutSrbOnList(xvdd, srb);
319 }
320 /* HandleEvent also puts queued SRB's on the ring */
321 XenVbd_HandleEvent(xvdd);
322 /* need 2 spare slots - 1 for EVENT and 1 for STOP/START */
323 if (xvsd->outstanding < 30) {
324 ScsiPortNotification(NextLuRequest, xvsd, 0, 0, 0);
325 } else {
326 ScsiPortNotification(NextRequest, xvsd);
327 }
328 /* if there was an error returned by an SRB then the queue will freeze. Queue a timer to resolve this */
329 if (xvsd->outstanding) {
330 ScsiPortNotification(RequestTimerCall, xvsd, XenVbd_HwScsiTimer, 100000);
331 } else {
332 ScsiPortNotification(RequestTimerCall, xvsd, XenVbd_HwScsiTimer, 0);
333 }
334 return TRUE;
335 }
337 static SCSI_ADAPTER_CONTROL_STATUS
338 XenVbd_HwScsiAdapterControl(PVOID DeviceExtension, SCSI_ADAPTER_CONTROL_TYPE ControlType, PVOID Parameters) {
339 PXENVBD_SCSIPORT_DATA xvsd = DeviceExtension;
340 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)xvsd->xvdd;
341 SCSI_ADAPTER_CONTROL_STATUS Status = ScsiAdapterControlSuccess;
342 PSCSI_SUPPORTED_CONTROL_TYPE_LIST SupportedControlTypeList;
344 FUNCTION_ENTER();
345 FUNCTION_MSG("IRQL = %d\n", KeGetCurrentIrql());
346 FUNCTION_MSG("xvsd = %p\n", xvsd);
348 switch (ControlType) {
349 case ScsiQuerySupportedControlTypes:
350 SupportedControlTypeList = (PSCSI_SUPPORTED_CONTROL_TYPE_LIST)Parameters;
351 FUNCTION_MSG("ScsiQuerySupportedControlTypes (Max = %d)\n", SupportedControlTypeList->MaxControlType);
352 SupportedControlTypeList->SupportedTypeList[ScsiQuerySupportedControlTypes] = TRUE;
353 SupportedControlTypeList->SupportedTypeList[ScsiStopAdapter] = TRUE;
354 SupportedControlTypeList->SupportedTypeList[ScsiRestartAdapter] = TRUE;
355 break;
356 case ScsiStopAdapter:
357 FUNCTION_MSG("ScsiStopAdapter\n");
358 if (xvdd->device_state == DEVICE_STATE_INACTIVE) {
359 FUNCTION_MSG("inactive - nothing to do\n");
360 break;
361 }
362 XN_ASSERT(IsListEmpty(&xvdd->srb_list));
363 //XN_ASSERT(xvdd->shadow_free == SHADOW_ENTRIES);
364 break;
365 case ScsiRestartAdapter:
366 FUNCTION_MSG("ScsiRestartAdapter\n");
367 if (xvdd->device_state == DEVICE_STATE_INACTIVE) {
368 FUNCTION_MSG("inactive - nothing to do\n");
369 break;
370 }
371 /* increase the tag every time we stop/start to track where the gref's came from */
372 xvdd->grant_tag++;
373 break;
374 case ScsiSetBootConfig:
375 FUNCTION_MSG("ScsiSetBootConfig\n");
376 break;
377 case ScsiSetRunningConfig:
378 FUNCTION_MSG("ScsiSetRunningConfig\n");
379 break;
380 default:
381 FUNCTION_MSG("UNKNOWN\n");
382 break;
383 }
385 FUNCTION_EXIT();
387 return Status;
388 }
390 NTSTATUS
391 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
392 ULONG status;
393 HW_INITIALIZATION_DATA HwInitializationData;
395 /* RegistryPath == NULL when we are invoked as a crash dump driver */
396 if (!RegistryPath) {
397 dump_mode = TRUE;
398 XnPrintDump();
399 }
401 FUNCTION_ENTER();
402 FUNCTION_MSG("IRQL = %d\n", KeGetCurrentIrql());
403 FUNCTION_MSG("DriverObject = %p, RegistryPath = %p\n", DriverObject, RegistryPath);
405 RtlZeroMemory(&HwInitializationData, sizeof(HW_INITIALIZATION_DATA));
406 HwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
407 HwInitializationData.AdapterInterfaceType = PNPBus; /* not Internal */
408 HwInitializationData.SrbExtensionSize = sizeof(srb_list_entry_t);
409 HwInitializationData.NumberOfAccessRanges = 1;
410 HwInitializationData.MapBuffers = TRUE;
411 HwInitializationData.NeedPhysicalAddresses = FALSE;
412 HwInitializationData.TaggedQueuing = TRUE;
413 HwInitializationData.AutoRequestSense = TRUE;
414 HwInitializationData.MultipleRequestPerLu = TRUE;
415 HwInitializationData.ReceiveEvent = FALSE;
416 HwInitializationData.HwInitialize = XenVbd_HwScsiInitialize;
417 HwInitializationData.HwStartIo = XenVbd_HwScsiStartIo;
418 HwInitializationData.HwFindAdapter = XenVbd_HwScsiFindAdapter;
419 HwInitializationData.HwResetBus = XenVbd_HwScsiResetBus;
420 HwInitializationData.HwAdapterControl = XenVbd_HwScsiAdapterControl;
421 if (!dump_mode) {
422 HwInitializationData.DeviceExtensionSize = FIELD_OFFSET(XENVBD_SCSIPORT_DATA, aligned_buffer_data) + UNALIGNED_BUFFER_DATA_SIZE;
423 } else {
424 HwInitializationData.HwInterrupt = XenVbd_HwScsiInterrupt;
425 HwInitializationData.DeviceExtensionSize = FIELD_OFFSET(XENVBD_SCSIPORT_DATA, aligned_buffer_data) + sizeof(XENVBD_DEVICE_DATA) + UNALIGNED_BUFFER_DATA_SIZE_DUMP_MODE;
426 }
427 status = ScsiPortInitialize(DriverObject, RegistryPath, &HwInitializationData, NULL);
429 if(!NT_SUCCESS(status)) {
430 FUNCTION_MSG("ScsiPortInitialize failed with status 0x%08x\n", status);
431 }
433 FUNCTION_EXIT();
435 return status;
436 }