win-pvdrivers

view xenpci/xenpci_pdo.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 27bd2a5a4704
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 #include "xenpci.h"
31 #include <stdlib.h>
32 #include <io/ring.h>
34 #pragma warning(disable : 4200) // zero-sized array
35 #pragma warning(disable: 4127) // conditional expression is constant
37 /* Not really necessary but keeps PREfast happy */
38 static EVT_WDF_INTERRUPT_SYNCHRONIZE XenPci_EvtChn_Sync_Routine;
39 static EVT_WDF_DEVICE_D0_ENTRY XenPciPdo_EvtDeviceD0Entry;
40 static EVT_WDF_DEVICE_D0_EXIT XenPciPdo_EvtDeviceD0Exit;
41 static EVT_WDF_DEVICE_USAGE_NOTIFICATION XenPciPdo_EvtDeviceUsageNotification;
42 static EVT_WDF_DEVICE_PNP_STATE_CHANGE_NOTIFICATION XenPci_EvtDevicePnpStateChange;
44 /*
45 Called at PASSIVE_LEVEL(?)
46 Called during restore
47 */
48 static ULONG
49 XenPci_ReadBackendState(PXENPCI_PDO_DEVICE_DATA xppdd)
50 {
51 PXENPCI_DEVICE_DATA xpdd = GetXpdd(xppdd->wdf_device_bus_fdo);
52 char path[128];
53 char *value;
54 char *err;
55 ULONG backend_state;
57 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
58 err = XenBus_Read(xpdd, XBT_NIL, path, &value);
59 if (err)
60 {
61 XenPci_FreeMem(err);
62 return XenbusStateUnknown;
63 }
64 else
65 {
66 backend_state = atoi(value);
67 XenPci_FreeMem(value);
68 return backend_state;
69 }
70 }
72 static NTSTATUS
73 XenPci_GetBackendDetails(WDFDEVICE device)
74 {
75 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(device);
76 PXENPCI_DEVICE_DATA xpdd = GetXpdd(xppdd->wdf_device_bus_fdo);
77 char path[128];
78 PCHAR res;
79 PCHAR value;
81 FUNCTION_ENTER();
82 /* Get backend path */
83 RtlStringCbPrintfA(path, ARRAY_SIZE(path),
84 "%s/backend", xppdd->path);
85 res = XenBus_Read(xpdd, XBT_NIL, path, &value);
86 if (res)
87 {
88 FUNCTION_MSG("Failed to read backend path\n");
89 XenPci_FreeMem(res);
90 return STATUS_UNSUCCESSFUL;
91 }
92 RtlStringCbCopyA(xppdd->backend_path, ARRAY_SIZE(xppdd->backend_path), value);
93 XenPci_FreeMem(value);
95 /* Get backend id */
96 RtlStringCbPrintfA(path, ARRAY_SIZE(path),
97 "%s/backend-id", xppdd->path);
98 res = XenBus_Read(xpdd, XBT_NIL, path, &value);
99 if (res) {
100 FUNCTION_MSG("Failed to read backend id\n");
101 XenPci_FreeMem(res);
102 return STATUS_UNSUCCESSFUL;
103 }
104 xppdd->backend_id = (domid_t)atoi(value);
105 XenPci_FreeMem(value);
106 FUNCTION_EXIT();
107 return STATUS_SUCCESS;
108 }
110 NTSTATUS
111 XenPci_SuspendPdo(WDFDEVICE device) {
112 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(device);
113 PXENPCI_DEVICE_DATA xpdd = xppdd->xpdd;
114 PCHAR response;
115 CHAR path[128];
117 if (xppdd->device_callback) {
118 FUNCTION_MSG("Suspending %s\n", xppdd->device);
119 xppdd->device_callback(xppdd->device_callback_context, XN_DEVICE_CALLBACK_SUSPEND, NULL);
120 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
121 response = XenBus_RemWatch(xpdd, XBT_NIL, path, XenPci_BackendStateCallback, xppdd);
122 if (response) {
123 FUNCTION_MSG("XnRemWatch - %s = %s\n", path, response);
124 XenPci_FreeMem(response);
125 }
126 }
127 return STATUS_SUCCESS;
128 }
130 NTSTATUS
131 XenPci_ResumePdo(WDFDEVICE device) {
132 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(device);
133 PXENPCI_DEVICE_DATA xpdd = xppdd->xpdd;
134 PCHAR response;
135 CHAR path[128];
137 XenPci_GetBackendDetails(device);
138 if (xppdd->device_callback) {
139 FUNCTION_MSG("Resuming %s\n", xppdd->device);
140 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
141 response = XenBus_AddWatch(xpdd, XBT_NIL, path, XenPci_BackendStateCallback, xppdd);
142 if (response) {
143 FUNCTION_MSG("XnAddWatch - %s = %s\n", path, response);
144 XenPci_FreeMem(response);
145 xppdd->device_callback = NULL;
146 xppdd->device_callback_context = NULL;
147 FUNCTION_EXIT();
148 return STATUS_UNSUCCESSFUL;
149 }
150 xppdd->device_callback(xppdd->device_callback_context, XN_DEVICE_CALLBACK_RESUME, NULL);
151 }
152 return STATUS_SUCCESS;
153 }
155 NTSTATUS
156 XenPciPdo_EvtDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previous_state) {
157 NTSTATUS status = STATUS_SUCCESS;
158 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(device);
159 PXENPCI_DEVICE_DATA xpdd = GetXpdd(xppdd->wdf_device_bus_fdo);
160 CHAR path[128];
162 FUNCTION_ENTER();
163 FUNCTION_MSG("path = %s\n", xppdd->path);
165 switch (previous_state) {
166 case WdfPowerDeviceD0:
167 FUNCTION_MSG("WdfPowerDeviceD1\n");
168 break;
169 case WdfPowerDeviceD1:
170 FUNCTION_MSG("WdfPowerDeviceD1\n");
171 break;
172 case WdfPowerDeviceD2:
173 FUNCTION_MSG("WdfPowerDeviceD2\n");
174 break;
175 case WdfPowerDeviceD3:
176 FUNCTION_MSG("WdfPowerDeviceD3\n");
177 if (xppdd->hiber_usage_kludge) {
178 FUNCTION_MSG("(but really WdfPowerDevicePrepareForHibernation)\n");
179 previous_state = WdfPowerDevicePrepareForHibernation;
180 }
181 break;
182 case WdfPowerDeviceD3Final:
183 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
184 break;
185 case WdfPowerDevicePrepareForHibernation:
186 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
187 break;
188 default:
189 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", previous_state);
190 break;
191 }
193 status = XenPci_GetBackendDetails(device);
194 if (!NT_SUCCESS(status)) {
195 WdfDeviceSetFailed(device, WdfDeviceFailedNoRestart);
196 FUNCTION_EXIT_STATUS(status);
197 return status;
198 }
200 if (previous_state == WdfPowerDevicePrepareForHibernation && xppdd->device_callback) {
201 FUNCTION_MSG("Restoring watch %s\n", xppdd->device);
202 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
203 XenBus_AddWatch(xpdd, XBT_NIL, path, XenPci_BackendStateCallback, xppdd);
204 }
206 if (!NT_SUCCESS(status)) {
207 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
208 //XenBus_RemWatch(xpdd, XBT_NIL, path, XenPci_BackendStateHandler, device);
209 WdfDeviceSetFailed(device, WdfDeviceFailedNoRestart);
210 FUNCTION_EXIT_STATUS(status);
211 return status;
212 }
214 FUNCTION_EXIT();
216 return status;
217 }
219 NTSTATUS
220 XenPciPdo_EvtDeviceD0Exit(WDFDEVICE device, WDF_POWER_DEVICE_STATE target_state) {
221 NTSTATUS status = STATUS_SUCCESS;
222 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(device);
223 PXENPCI_DEVICE_DATA xpdd = GetXpdd(xppdd->wdf_device_bus_fdo);
224 char path[128];
226 UNREFERENCED_PARAMETER(device);
227 UNREFERENCED_PARAMETER(target_state);
229 FUNCTION_ENTER();
230 FUNCTION_MSG("path = %s\n", xppdd->path);
232 switch (target_state) {
233 case WdfPowerDeviceD0:
234 FUNCTION_MSG("WdfPowerDeviceD1\n");
235 break;
236 case WdfPowerDeviceD1:
237 FUNCTION_MSG("WdfPowerDeviceD1\n");
238 break;
239 case WdfPowerDeviceD2:
240 FUNCTION_MSG("WdfPowerDeviceD2\n");
241 break;
242 case WdfPowerDeviceD3:
243 FUNCTION_MSG("WdfPowerDeviceD3\n");
244 if (xppdd->hiber_usage_kludge) {
245 FUNCTION_MSG("(but really WdfPowerDevicePrepareForHibernation)\n");
246 target_state = WdfPowerDevicePrepareForHibernation;
247 }
248 break;
249 case WdfPowerDeviceD3Final:
250 FUNCTION_MSG("WdfPowerDeviceD3Final\n");
251 break;
252 case WdfPowerDevicePrepareForHibernation:
253 FUNCTION_MSG("WdfPowerDevicePrepareForHibernation\n");
254 break;
255 default:
256 FUNCTION_MSG("Unknown WdfPowerDevice state %d\n", target_state);
257 break;
258 }
260 if (target_state == WdfPowerDevicePrepareForHibernation)
261 {
262 FUNCTION_MSG("not powering down as we are hibernating\n");
263 // should we set the backend state here so it's correct on resume???
264 }
266 /* Remove watch on backend state */
267 /* even if hibernate */
268 if (xppdd->device_callback) {
269 FUNCTION_MSG("Removing watch %s\n", xppdd->device);
270 RtlStringCbPrintfA(path, ARRAY_SIZE(path), "%s/state", xppdd->backend_path);
271 XenBus_RemWatch(xpdd, XBT_NIL, path, XenPci_BackendStateCallback, xppdd);
272 }
273 FUNCTION_EXIT();
275 return status;
276 }
278 static VOID
279 XenPciPdo_EvtDeviceUsageNotification(WDFDEVICE device, WDF_SPECIAL_FILE_TYPE notification_type, BOOLEAN is_in_notification_path)
280 {
281 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(device);
283 FUNCTION_ENTER();
285 FUNCTION_MSG("path = %s\n", xppdd->path);
286 switch (notification_type)
287 {
288 case WdfSpecialFilePaging:
289 FUNCTION_MSG("notification_type = Paging, flag = %d\n", is_in_notification_path);
290 break;
291 case WdfSpecialFileHibernation:
292 xppdd->hiber_usage_kludge = is_in_notification_path;
293 FUNCTION_MSG("notification_type = Hibernation, flag = %d\n", is_in_notification_path);
294 break;
295 case WdfSpecialFileDump:
296 FUNCTION_MSG("notification_type = Dump, flag = %d\n", is_in_notification_path);
297 break;
298 default:
299 FUNCTION_MSG("notification_type = %d, flag = %d\n", notification_type, is_in_notification_path);
300 break;
301 }
303 FUNCTION_EXIT();
304 }
306 static VOID
307 XenPci_EvtDevicePnpStateChange(WDFDEVICE device, PCWDF_DEVICE_PNP_NOTIFICATION_DATA notification_data)
308 {
309 PXENPCI_PDO_DEVICE_DATA xppdd = GetXppdd(device);
311 //FUNCTION_ENTER();
313 if (xppdd->backend_initiated_remove
314 && notification_data->Type == StateNotificationEnterState
315 && notification_data->Data.EnterState.CurrentState == WdfDevStatePnpQueryRemovePending
316 && notification_data->Data.EnterState.NewState == WdfDevStatePnpQueryCanceled)
317 {
318 PXENPCI_DEVICE_DATA xpdd = GetXpdd(xppdd->wdf_device_bus_fdo);
320 FUNCTION_MSG("Eject failed, doing surprise removal\n");
321 xppdd->do_not_enumerate = TRUE;
322 XenPci_EvtChildListScanForChildren(xpdd->child_list);
323 }
325 //FUNCTION_EXIT();
327 //return STATUS_SUCCESS;
328 }
330 NTSTATUS
331 XenPci_EvtChildListCreateDevice(WDFCHILDLIST child_list,
332 PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER identification_header,
333 PWDFDEVICE_INIT child_init) {
334 NTSTATUS status = STATUS_SUCCESS;
335 WDF_OBJECT_ATTRIBUTES child_attributes;
336 WDFDEVICE child_device;
337 PXENPCI_PDO_IDENTIFICATION_DESCRIPTION identification = (PXENPCI_PDO_IDENTIFICATION_DESCRIPTION)identification_header;
338 WDF_DEVICE_PNP_CAPABILITIES child_pnp_capabilities;
339 DECLARE_UNICODE_STRING_SIZE(buffer, 512);
340 DECLARE_CONST_UNICODE_STRING(location, L"Xen Bus");
341 PXENPCI_PDO_DEVICE_DATA xppdd;
342 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfChildListGetDevice(child_list));
343 //WDF_PDO_EVENT_CALLBACKS pdo_callbacks;
344 WDF_PNPPOWER_EVENT_CALLBACKS child_pnp_power_callbacks;
345 //UCHAR pnp_minor_functions[] = { IRP_MN_START_DEVICE };
346 WDF_DEVICE_POWER_CAPABILITIES child_power_capabilities;
348 FUNCTION_ENTER();
350 WdfDeviceInitSetDeviceType(child_init, FILE_DEVICE_UNKNOWN);
352 WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&child_pnp_power_callbacks);
353 child_pnp_power_callbacks.EvtDeviceD0Entry = XenPciPdo_EvtDeviceD0Entry;
354 child_pnp_power_callbacks.EvtDeviceD0Exit = XenPciPdo_EvtDeviceD0Exit;
355 child_pnp_power_callbacks.EvtDeviceUsageNotification = XenPciPdo_EvtDeviceUsageNotification;
356 WdfDeviceInitSetPnpPowerEventCallbacks(child_init, &child_pnp_power_callbacks);
358 FUNCTION_MSG("device = '%s', index = '%d', path = '%s'\n",
359 identification->device, identification->index, identification->path);
361 //status = WdfDeviceInitAssignWdmIrpPreprocessCallback(child_init, XenPciPdo_EvtDeviceWdmIrpPreprocess_START_DEVICE,
362 // IRP_MJ_PNP, pnp_minor_functions, ARRAY_SIZE(pnp_minor_functions));
363 //if (!NT_SUCCESS(status)) {
364 // return status;
365 //}
367 //WDF_PDO_EVENT_CALLBACKS_INIT(&pdo_callbacks);
368 //pdo_callbacks.EvtDeviceResourcesQuery = XenPciPdo_EvtDeviceResourcesQuery;
369 //pdo_callbacks.EvtDeviceResourceRequirementsQuery = XenPciPdo_EvtDeviceResourceRequirementsQuery;
370 //pdo_callbacks.EvtDeviceEject = XenPciPdo_EvtDeviceEject;
371 //pdo_callbacks.EvtDeviceSetLock = XenPciPdo_EvtDeviceSetLock;
372 //WdfPdoInitSetEventCallbacks(child_init, &pdo_callbacks);
374 RtlUnicodeStringPrintf(&buffer, L"xen\\%S", identification->device);
375 status = WdfPdoInitAssignDeviceID(child_init, &buffer);
376 if (!NT_SUCCESS(status))
377 {
378 return status;
379 }
380 status = WdfPdoInitAddHardwareID(child_init, &buffer);
381 if (!NT_SUCCESS(status))
382 {
383 return status;
384 }
385 status = WdfPdoInitAddCompatibleID(child_init, &buffer);
386 if (!NT_SUCCESS(status))
387 {
388 return status;
389 }
391 RtlUnicodeStringPrintf(&buffer, L"%02d", identification->index);
392 status = WdfPdoInitAssignInstanceID(child_init, &buffer);
393 if (!NT_SUCCESS(status))
394 {
395 return status;
396 }
398 RtlUnicodeStringPrintf(&buffer, L"Xen %S device #%d", identification->device, identification->index);
399 status = WdfPdoInitAddDeviceText(child_init, &buffer, &location, 0x0409);
400 if (!NT_SUCCESS(status))
401 {
402 return status;
403 }
404 WdfPdoInitSetDefaultLocale(child_init, 0x0409);
406 WdfDeviceInitSetPowerNotPageable(child_init);
408 WdfDeviceInitRegisterPnpStateChangeCallback(child_init, WdfDevStatePnpQueryCanceled, XenPci_EvtDevicePnpStateChange, StateNotificationEnterState);
410 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&child_attributes, XENPCI_PDO_DEVICE_DATA);
411 status = WdfDeviceCreate(&child_init, &child_attributes, &child_device);
412 if (!NT_SUCCESS(status))
413 {
414 return status;
415 }
417 xppdd = GetXppdd(child_device);
419 xppdd->wdf_device = child_device;
420 xppdd->wdf_device_bus_fdo = WdfChildListGetDevice(child_list);
421 xppdd->xpdd = xpdd;
423 WdfDeviceSetSpecialFileSupport(child_device, WdfSpecialFilePaging, TRUE);
424 WdfDeviceSetSpecialFileSupport(child_device, WdfSpecialFileHibernation, TRUE);
425 WdfDeviceSetSpecialFileSupport(child_device, WdfSpecialFileDump, TRUE);
427 WDF_DEVICE_PNP_CAPABILITIES_INIT(&child_pnp_capabilities);
428 child_pnp_capabilities.LockSupported = WdfFalse;
429 child_pnp_capabilities.EjectSupported = WdfTrue;
430 child_pnp_capabilities.Removable = WdfTrue;
431 child_pnp_capabilities.DockDevice = WdfFalse;
432 child_pnp_capabilities.UniqueID = WdfFalse;
433 child_pnp_capabilities.SilentInstall = WdfTrue;
434 child_pnp_capabilities.SurpriseRemovalOK = WdfTrue;
435 child_pnp_capabilities.HardwareDisabled = WdfFalse;
436 WdfDeviceSetPnpCapabilities(child_device, &child_pnp_capabilities);
438 WDF_DEVICE_POWER_CAPABILITIES_INIT(&child_power_capabilities);
439 child_power_capabilities.DeviceD1 = WdfTrue;
440 child_power_capabilities.WakeFromD1 = WdfTrue;
441 child_power_capabilities.DeviceWake = PowerDeviceD1;
442 child_power_capabilities.DeviceState[PowerSystemWorking] = PowerDeviceD0;
443 child_power_capabilities.DeviceState[PowerSystemSleeping1] = PowerDeviceD1;
444 child_power_capabilities.DeviceState[PowerSystemSleeping2] = PowerDeviceD2;
445 child_power_capabilities.DeviceState[PowerSystemSleeping3] = PowerDeviceD2;
446 child_power_capabilities.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
447 child_power_capabilities.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
448 WdfDeviceSetPowerCapabilities(child_device, &child_power_capabilities);
450 RtlStringCbCopyA(xppdd->path, ARRAY_SIZE(xppdd->path), identification->path);
451 RtlStringCbCopyA(xppdd->device, ARRAY_SIZE(xppdd->device), identification->device);
452 xppdd->index = identification->index;
453 KeInitializeEvent(&xppdd->backend_state_event, SynchronizationEvent, FALSE);
454 ExInitializeFastMutex(&xppdd->backend_state_mutex);
455 xppdd->backend_state = XenbusStateUnknown;
456 xppdd->frontend_state = XenbusStateUnknown;
457 xppdd->backend_path[0] = '\0';
458 xppdd->backend_id = 0;
460 FUNCTION_EXIT();
462 return status;