win-pvdrivers

view xenvbd_filter/xenvbd_filter.c @ 1060:b448f01b31e8

Added diagnostics for slow vbd events
author James Harper <james.harper@bendigoit.com.au>
date Tue Oct 01 13:46:32 2013 +1000 (2013-10-01)
parents 0f40ce5cb467
children 5775fb8612ab
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
3 Copyright (C) 2013 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 #include "xenvbd_filter.h"
21 #include "ntddscsi.h"
22 #include "srb.h"
24 DRIVER_INITIALIZE DriverEntry;
26 static EVT_WDF_DRIVER_UNLOAD XenVbd_EvtDriverUnload;
27 static EVT_WDF_DRIVER_DEVICE_ADD XenVbd_EvtDeviceAdd;
28 static EVT_WDF_REQUEST_COMPLETION_ROUTINE XenVbd_SendRequestComplete;
29 static EVT_WDF_DEVICE_D0_ENTRY XenVbd_EvtDeviceD0Entry;
30 static EVT_WDF_DEVICE_D0_EXIT XenVbd_EvtDeviceD0Exit;
31 static EVT_WDFDEVICE_WDM_IRP_PREPROCESS XenVbd_EvtDeviceWdmIrpPreprocess_START_DEVICE;
32 static EVT_WDFDEVICE_WDM_IRP_PREPROCESS XenVbd_EvtDeviceWdmIrpPreprocess_SET_POWER;
33 static EVT_WDF_DPC XenVbd_EvtDpcEvent;
34 static IO_COMPLETION_ROUTINE XenVbd_IoCompletion_START_DEVICE;
36 static VOID XenVbd_DeviceCallback(PVOID context, ULONG callback_type, PVOID value);
37 static VOID XenVbd_HandleEventDIRQL(PVOID context);
38 static VOID XenVbd_StopRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend);
39 static VOID XenVbd_StartRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend);
41 #include "../xenvbd_common/common_xen.h"
43 static VOID XenVbd_SendEvent(WDFDEVICE device);
45 static VOID
46 XenVbd_StopRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend) {
47 PXENVBD_FILTER_DATA xvfd = (PXENVBD_FILTER_DATA)xvdd->xvfd;
48 NTSTATUS status;
49 WDFREQUEST request;
50 WDF_REQUEST_SEND_OPTIONS send_options;
51 IO_STACK_LOCATION stack;
52 SCSI_REQUEST_BLOCK srb;
53 SRB_IO_CONTROL sic;
55 FUNCTION_ENTER();
57 /* send a 'stop' down if we are suspending */
58 if (suspend) {
59 status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, xvfd->wdf_target, &request);
60 FUNCTION_MSG("WdfRequestCreate = %08x\n", status);
62 RtlZeroMemory(&stack, sizeof(IO_STACK_LOCATION));
63 stack.MajorFunction = IRP_MJ_SCSI;
64 stack.MinorFunction = IRP_MN_SCSI_CLASS;
65 stack.Parameters.Scsi.Srb = &srb;
67 RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
68 srb.SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
69 srb.Length = SCSI_REQUEST_BLOCK_SIZE;
70 srb.PathId = 0;
71 srb.TargetId = 0;
72 srb.Lun = 0;
73 srb.OriginalRequest = WdfRequestWdmGetIrp(request);
74 srb.Function = SRB_FUNCTION_IO_CONTROL;
75 srb.DataBuffer = &sic;
77 RtlZeroMemory(&sic, sizeof(SRB_IO_CONTROL));
78 sic.HeaderLength = sizeof(SRB_IO_CONTROL);
79 memcpy(sic.Signature, XENVBD_CONTROL_SIG, 8);
80 sic.Timeout = 60;
81 sic.ControlCode = XENVBD_CONTROL_STOP;
83 WdfRequestWdmFormatUsingStackLocation(request, &stack);
85 WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
86 if (!WdfRequestSend(request, xvfd->wdf_target, &send_options)) {
87 FUNCTION_MSG("Request was _NOT_ sent\n");
88 }
89 status = WdfRequestGetStatus(request);
90 FUNCTION_MSG("Request Status = %08x\n", status);
91 FUNCTION_MSG("SRB Status = %08x\n", srb.SrbStatus);
93 WdfObjectDelete(request);
94 }
96 status = XnWriteInt32(xvdd->handle, XN_BASE_FRONTEND, "state", XenbusStateClosing);
98 FUNCTION_EXIT();
99 }
101 static VOID
102 XenVbd_StartRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend) {
103 PXENVBD_FILTER_DATA xvfd = (PXENVBD_FILTER_DATA)xvdd->xvfd;
104 NTSTATUS status;
105 WDFREQUEST request;
106 WDF_REQUEST_SEND_OPTIONS send_options;
107 IO_STACK_LOCATION stack;
108 SCSI_REQUEST_BLOCK srb;
109 SRB_IO_CONTROL sic;
111 FUNCTION_ENTER();
113 /* send a 'start' down if we are resuming from a suspend */
114 if (suspend) {
115 status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, xvfd->wdf_target, &request);
116 FUNCTION_MSG("WdfRequestCreate = %08x\n", status);
118 RtlZeroMemory(&stack, sizeof(IO_STACK_LOCATION));
119 stack.MajorFunction = IRP_MJ_SCSI;
120 stack.Parameters.Scsi.Srb = &srb;
122 RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
123 srb.SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
124 srb.Length = SCSI_REQUEST_BLOCK_SIZE;
125 srb.PathId = 0;
126 srb.TargetId = 0;
127 srb.Lun = 0;
128 srb.OriginalRequest = WdfRequestWdmGetIrp(request);
129 srb.Function = SRB_FUNCTION_IO_CONTROL;
130 srb.DataBuffer = &sic;
132 RtlZeroMemory(&sic, sizeof(SRB_IO_CONTROL));
133 sic.HeaderLength = sizeof(SRB_IO_CONTROL);
134 memcpy(sic.Signature, XENVBD_CONTROL_SIG, 8);
135 sic.Timeout = 60;
136 sic.ControlCode = XENVBD_CONTROL_START;
138 WdfRequestWdmFormatUsingStackLocation(request, &stack);
140 WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
141 if (!WdfRequestSend(request, xvfd->wdf_target, &send_options)) {
142 FUNCTION_MSG("Request was _NOT_ sent\n");
143 }
144 status = WdfRequestGetStatus(request);
145 FUNCTION_MSG("Request Status = %08x\n", status);
146 FUNCTION_MSG("SRB Status = %08x\n", srb.SrbStatus);
148 WdfObjectDelete(request);
149 }
151 FUNCTION_EXIT();
152 }
154 static VOID
155 XenVbd_SendRequestComplete(WDFREQUEST request, WDFIOTARGET target, PWDF_REQUEST_COMPLETION_PARAMS params, WDFCONTEXT context) {
156 NTSTATUS status;
157 PSCSI_REQUEST_BLOCK srb = context;
158 LARGE_INTEGER systemtime;
159 ULONGLONG elapsed;
161 UNREFERENCED_PARAMETER(target);
162 UNREFERENCED_PARAMETER(params);
163 UNREFERENCED_PARAMETER(context);
165 status = WdfRequestGetStatus(request);
166 if (status != 0 || srb->SrbStatus != SRB_STATUS_SUCCESS) {
167 FUNCTION_MSG("Request Status = %08x, SRB Status = %08x\n", status, srb->SrbStatus);
168 }
169 KeQuerySystemTime(&systemtime);
170 elapsed = systemtime.QuadPart - ((PLARGE_INTEGER)((PUCHAR)context + sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL)))->QuadPart;
171 elapsed = elapsed / 10000; // now in ms
172 if (elapsed > 1000) {
173 FUNCTION_MSG("Event took %d ms\n", (ULONG)elapsed);
174 }
175 ExFreePoolWithTag(context, XENVBD_POOL_TAG);
176 WdfObjectDelete(request);
177 }
179 static VOID
180 XenVbd_SendEvent(WDFDEVICE device) {
181 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
182 NTSTATUS status;
183 WDFREQUEST request;
184 WDF_REQUEST_SEND_OPTIONS send_options;
185 IO_STACK_LOCATION stack;
186 PUCHAR buf;
187 PSCSI_REQUEST_BLOCK srb;
188 PSRB_IO_CONTROL sic;
190 status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, xvfd->wdf_target, &request);
191 if (status != STATUS_SUCCESS) {
192 FUNCTION_MSG("WdfRequestCreate failed %08x\n", status);
193 /* this is bad - event will be dropped */
194 return;
195 }
197 buf = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL) + sizeof(LARGE_INTEGER), XENVBD_POOL_TAG);
198 RtlZeroMemory(buf, sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL));
199 srb = (PSCSI_REQUEST_BLOCK)(buf);
200 sic = (PSRB_IO_CONTROL)(buf + sizeof(SCSI_REQUEST_BLOCK));
202 srb->Length = sizeof(SCSI_REQUEST_BLOCK);
203 srb->SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
204 srb->PathId = 0;
205 srb->TargetId = 0;
206 srb->Lun = 0;
207 srb->OriginalRequest = WdfRequestWdmGetIrp(request);
208 srb->Function = SRB_FUNCTION_IO_CONTROL;
209 srb->DataBuffer = sic;
210 srb->DataTransferLength = sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL);
211 srb->TimeOutValue = (ULONG)-1;
213 sic->HeaderLength = sizeof(SRB_IO_CONTROL);
214 memcpy(sic->Signature, XENVBD_CONTROL_SIG, 8);
215 sic->Timeout = (ULONG)-1;
216 sic->ControlCode = XENVBD_CONTROL_EVENT;
218 KeQuerySystemTime((PLARGE_INTEGER)((PUCHAR)buf + sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL)));
220 RtlZeroMemory(&stack, sizeof(IO_STACK_LOCATION));
221 stack.MajorFunction = IRP_MJ_SCSI;
222 stack.MinorFunction = IRP_MN_SCSI_CLASS;
223 stack.Parameters.Scsi.Srb = srb;
225 WdfRequestWdmFormatUsingStackLocation(request, &stack);
226 WdfRequestSetCompletionRoutine(request, XenVbd_SendRequestComplete, buf);
228 WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, 0); //WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE);
229 if (!WdfRequestSend(request, xvfd->wdf_target, &send_options)) {
230 FUNCTION_MSG("Error sending request\n");
231 }
232 }
234 static VOID
235 XenVbd_EvtDpcEvent(WDFDPC dpc) {
236 WDFDEVICE device = WdfDpcGetParentObject(dpc);
238 XenVbd_SendEvent(device);
239 }
241 static VOID
242 XenVbd_HandleEventDIRQL(PVOID context) {
243 PXENVBD_DEVICE_DATA xvdd = (PXENVBD_DEVICE_DATA)context;
244 PXENVBD_FILTER_DATA xvfd = (PXENVBD_FILTER_DATA)xvdd->xvfd;
245 WdfDpcEnqueue(xvfd->dpc);
246 }
248 static NTSTATUS
249 XenVbd_EvtDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state) {
250 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
251 NTSTATUS status;
253 UNREFERENCED_PARAMETER(previous_state);
254 // if waking from hibernate then same as suspend... maybe?
255 FUNCTION_ENTER();
256 status = XenVbd_Connect(&xvfd->xvdd, FALSE);
257 FUNCTION_EXIT();
258 return status;
259 }
261 static NTSTATUS
262 XenVbd_EvtDeviceD0Exit(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state) {
263 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
264 NTSTATUS status = STATUS_SUCCESS;
266 FUNCTION_ENTER();
267 switch (target_state) {
268 case WdfPowerDeviceD0:
269 FUNCTION_MSG("WdfPowerDeviceD1\n");
270 break;
271 case WdfPowerDeviceD1:
272 FUNCTION_MSG("WdfPowerDeviceD1\n");
273 break;
274 case WdfPowerDeviceD2:
275 FUNCTION_MSG("WdfPowerDeviceD2\n");
276 break;
277 case WdfPowerDeviceD3:
278 FUNCTION_MSG("WdfPowerDeviceD3\n");
279 if (xvfd->hibernate_flag) {
280 FUNCTION_MSG("(but really WdfPowerDevicePrepareForHibernation)\n");
281 target_state = WdfPowerDevicePrepareForHibernation;
282 }
283 break;
284 case WdfPowerDeviceD3Final:
285 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
286 break;
287 case WdfPowerDevicePrepareForHibernation:
288 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
289 break;
290 default:
291 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
292 break;
293 }
294 if (target_state != WdfPowerDevicePrepareForHibernation) {
295 status = XenVbd_Disconnect(&xvfd->xvdd, FALSE);
296 }
297 FUNCTION_EXIT();
298 return status;
299 }
301 static NTSTATUS
302 XenVbd_IoCompletion_START_DEVICE(PDEVICE_OBJECT device, PIRP irp, PVOID context) {
303 UNREFERENCED_PARAMETER(device);
304 UNREFERENCED_PARAMETER(irp);
305 FUNCTION_ENTER();
306 ExFreePoolWithTag(context, XENVBD_POOL_TAG);
307 FUNCTION_EXIT();
308 return STATUS_SUCCESS;
309 }
311 static NTSTATUS
312 XenVbd_EvtDeviceWdmIrpPreprocess_START_DEVICE(WDFDEVICE device, PIRP irp) {
313 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
314 PIO_STACK_LOCATION stack;
315 PCM_RESOURCE_LIST crl;
316 PCM_FULL_RESOURCE_DESCRIPTOR cfrd;
317 PCM_PARTIAL_RESOURCE_LIST cprl;
318 PCM_PARTIAL_RESOURCE_DESCRIPTOR prd;
320 FUNCTION_ENTER();
322 /*
323 Pass down the xvdd area as a memory resource. This gives xenvbd the data in a known place
324 and also satisifies the scsiport requirement for a memory resource
325 */
326 IoCopyCurrentIrpStackLocationToNext(irp);
327 stack = IoGetNextIrpStackLocation(irp);
329 crl = ExAllocatePoolWithTag(NonPagedPool,
330 FIELD_OFFSET(CM_RESOURCE_LIST, List) +
331 FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList) +
332 FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST, PartialDescriptors) +
333 sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * 1, XENVBD_POOL_TAG);
334 if (!crl) {
335 // TODO: Fail this correctly
336 }
337 crl->Count = 1;
338 cfrd = &crl->List[0];
339 cfrd->InterfaceType = PNPBus;
340 cfrd->BusNumber = 0;
341 cprl = &cfrd->PartialResourceList;
342 cprl->Version = 1;
343 cprl->Revision = 1;
344 cprl->Count = 1;
345 prd = &cprl->PartialDescriptors[0];
346 prd->Type = CmResourceTypeMemory;
347 prd->ShareDisposition = CmResourceShareShared;
348 prd->Flags = CM_RESOURCE_MEMORY_READ_WRITE | CM_RESOURCE_MEMORY_CACHEABLE;
349 prd->u.Memory.Start.QuadPart = (ULONG_PTR)&xvfd->xvdd;
350 prd->u.Memory.Length = sizeof(XENVBD_DEVICE_DATA);
351 stack->Parameters.StartDevice.AllocatedResources = crl;
352 stack->Parameters.StartDevice.AllocatedResourcesTranslated = crl;
354 IoSetCompletionRoutine(irp, XenVbd_IoCompletion_START_DEVICE, crl, TRUE, TRUE, TRUE);
356 FUNCTION_EXIT();
358 return WdfDeviceWdmDispatchPreprocessedIrp(device, irp);
359 }
361 /* scsiport doesn't process SET_POWER correctly so we have to fudge detection of hibernate */
362 static NTSTATUS
363 XenVbd_EvtDeviceWdmIrpPreprocess_SET_POWER(WDFDEVICE device, PIRP irp) {
364 PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
365 PIO_STACK_LOCATION stack;
367 FUNCTION_ENTER();
368 stack = IoGetCurrentIrpStackLocation(irp);
369 if (stack->Parameters.Power.Type == DevicePowerState && stack->Parameters.Power.State.DeviceState == PowerDeviceD3 && stack->Parameters.Power.ShutdownType == PowerActionHibernate) {
370 FUNCTION_MSG("Going to hibernate\n");
371 xvfd->hibernate_flag = TRUE;
372 } else {
373 xvfd->hibernate_flag = FALSE;
374 }
375 IoSkipCurrentIrpStackLocation(irp);
376 FUNCTION_EXIT();
377 return WdfDeviceWdmDispatchPreprocessedIrp(device, irp);
378 }
380 static NTSTATUS
381 XenVbd_EvtDeviceAdd(WDFDRIVER driver, PWDFDEVICE_INIT device_init) {
382 PXENVBD_FILTER_DATA xvfd;
383 NTSTATUS status;
384 WDFDEVICE device;
385 WDF_OBJECT_ATTRIBUTES device_attributes;
386 WDF_PNPPOWER_EVENT_CALLBACKS pnp_power_callbacks;
387 WDF_DPC_CONFIG dpc_config;
388 WDF_OBJECT_ATTRIBUTES oa;
389 UCHAR pnp_minor_functions[] = { IRP_MN_START_DEVICE };
390 UCHAR power_minor_functions[] = { IRP_MN_SET_POWER };
392 UNREFERENCED_PARAMETER(driver);
394 FUNCTION_ENTER();
396 WdfDeviceInitSetDeviceType(device_init, FILE_DEVICE_UNKNOWN);
398 WdfFdoInitSetFilter(device_init);
400 WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnp_power_callbacks);
401 pnp_power_callbacks.EvtDeviceD0Entry = XenVbd_EvtDeviceD0Entry;
402 pnp_power_callbacks.EvtDeviceD0Exit = XenVbd_EvtDeviceD0Exit;
403 WdfDeviceInitSetPnpPowerEventCallbacks(device_init, &pnp_power_callbacks);
405 status = WdfDeviceInitAssignWdmIrpPreprocessCallback(device_init, XenVbd_EvtDeviceWdmIrpPreprocess_START_DEVICE,
406 IRP_MJ_PNP, pnp_minor_functions, ARRAY_SIZE(pnp_minor_functions));
407 if (!NT_SUCCESS(status)) {
408 return status;
409 }
411 status = WdfDeviceInitAssignWdmIrpPreprocessCallback(device_init, XenVbd_EvtDeviceWdmIrpPreprocess_SET_POWER,
412 IRP_MJ_POWER, power_minor_functions, ARRAY_SIZE(power_minor_functions));
413 if (!NT_SUCCESS(status)) {
414 return status;
415 }
417 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&device_attributes, XENVBD_FILTER_DATA);
418 status = WdfDeviceCreate(&device_init, &device_attributes, &device);
419 if (!NT_SUCCESS(status)) {
420 FUNCTION_MSG("Error creating device 0x%x\n", status);
421 return status;
422 }
424 xvfd = GetXvfd(device);
425 xvfd->wdf_device = device;
426 xvfd->wdf_target = WdfDeviceGetIoTarget(device);
427 xvfd->xvdd.xvfd = xvfd;
428 xvfd->xvdd.pdo = WdfDeviceWdmGetPhysicalDevice(device);
429 xvfd->xvdd.grant_tag = XENVBD_POOL_TAG;
431 KeInitializeEvent(&xvfd->xvdd.backend_event, SynchronizationEvent, FALSE);
433 WDF_DPC_CONFIG_INIT(&dpc_config, XenVbd_EvtDpcEvent);
434 WDF_OBJECT_ATTRIBUTES_INIT(&oa);
435 oa.ParentObject = device;
436 status = WdfDpcCreate(&dpc_config, &oa, &xvfd->dpc);
438 WdfDeviceSetSpecialFileSupport(device, WdfSpecialFilePaging, TRUE);
439 WdfDeviceSetSpecialFileSupport(device, WdfSpecialFileHibernation, TRUE);
440 WdfDeviceSetSpecialFileSupport(device, WdfSpecialFileDump, TRUE);
442 FUNCTION_EXIT();
443 return status;
444 }
446 NTSTATUS
447 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
448 NTSTATUS status;
449 WDF_DRIVER_CONFIG config;
450 WDFDRIVER driver;
452 UNREFERENCED_PARAMETER(RegistryPath);
454 FUNCTION_ENTER();
456 WDF_DRIVER_CONFIG_INIT(&config, XenVbd_EvtDeviceAdd);
457 status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver);
458 if (!NT_SUCCESS(status)) {
459 FUNCTION_MSG("WdfDriverCreate failed with status 0x%x\n", status);
460 FUNCTION_EXIT();
461 return status;
462 }
463 FUNCTION_EXIT();
464 return status;
465 }