win-pvdrivers

view xenpci/xenbus_device_interface.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 5bdb7251370c
children
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 #include "xenpci.h"
22 /* Not really necessary but keeps PREfast happy */
23 static EVT_WDF_FILE_CLEANUP XenBus_EvtFileCleanup;
24 static EVT_WDF_FILE_CLOSE XenBus_EvtFileClose;
25 static EVT_WDF_IO_QUEUE_IO_READ XenBus_EvtIoRead;
26 static EVT_WDF_IO_QUEUE_IO_WRITE XenBus_EvtIoWrite;
28 typedef struct {
29 LIST_ENTRY entry;
30 PVOID data;
31 ULONG length;
32 ULONG offset;
33 } xenbus_read_queue_item_t;
35 typedef struct
36 {
37 LIST_ENTRY entry;
38 CHAR path[128];
39 CHAR token[128];
40 WDFFILEOBJECT file_object;
41 } watch_context_t;
43 static VOID
44 XenBus_ProcessReadRequest(WDFQUEUE queue, WDFREQUEST request, size_t length)
45 {
46 NTSTATUS status;
47 WDFFILEOBJECT file_object = WdfRequestGetFileObject(request);
48 PXENPCI_DEVICE_INTERFACE_DATA xpdid = GetXpdid(file_object);
49 ULONG dst_length = (ULONG)length;
50 ULONG dst_offset = 0;
51 ULONG copy_length;
52 xenbus_read_queue_item_t *list_entry;
53 PVOID buffer;
55 UNREFERENCED_PARAMETER(queue);
57 status = WdfRequestRetrieveOutputBuffer(request, length, &buffer, NULL);
58 if (!NT_SUCCESS(status))
59 {
60 KdPrint((__DRIVER_NAME, " WdfRequestRetrieveOutputBuffer failed status = %08x\n", status));
61 WdfRequestSetInformation(request, 0);
62 return;
63 }
64 ASSERT(NT_SUCCESS(status)); // lazy?
66 while(dst_offset < dst_length && (list_entry = (xenbus_read_queue_item_t *)RemoveHeadList(&xpdid->xenbus.read_list_head)) != (xenbus_read_queue_item_t *)&xpdid->xenbus.read_list_head)
67 {
68 copy_length = min(list_entry->length - list_entry->offset, dst_length - dst_offset);
69 memcpy((PUCHAR)buffer + dst_offset, (PUCHAR)list_entry->data + list_entry->offset, copy_length);
70 list_entry->offset += copy_length;
71 dst_offset += copy_length;
72 if (list_entry->offset == list_entry->length)
73 {
74 ExFreePoolWithTag(list_entry->data, XENPCI_POOL_TAG);
75 ExFreePoolWithTag(list_entry, XENPCI_POOL_TAG);
76 }
77 else
78 {
79 InsertHeadList(&xpdid->xenbus.read_list_head, (PLIST_ENTRY)list_entry);
80 }
81 }
82 WdfRequestSetInformation(request, dst_offset);
84 FUNCTION_EXIT();
85 }
87 static VOID
88 XenPci_IoWatch(char *path, PVOID context)
89 {
90 NTSTATUS status;
91 watch_context_t *watch_context = context;
92 WDFFILEOBJECT file_object = watch_context->file_object;
93 PXENPCI_DEVICE_INTERFACE_DATA xpdid = GetXpdid(file_object);
94 KIRQL old_irql;
95 struct xsd_sockmsg *rep;
96 xenbus_read_queue_item_t *list_entry;
97 size_t remaining;
98 WDFREQUEST request;
100 FUNCTION_ENTER();
102 KeAcquireSpinLock(&xpdid->lock, &old_irql);
104 remaining = sizeof(struct xsd_sockmsg) + strlen(path) + 1 + strlen(watch_context->token) + 1;
105 rep = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct xsd_sockmsg) + strlen(path) + 1 + strlen(watch_context->token) + 1, XENPCI_POOL_TAG);
106 rep->type = XS_WATCH_EVENT;
107 rep->req_id = 0;
108 rep->tx_id = 0;
109 rep->len = (ULONG)(strlen(path) + 1 + strlen(watch_context->token) + 1);
110 remaining -= sizeof(struct xsd_sockmsg);
111 RtlStringCbCopyA((PCHAR)(rep + 1), remaining, path);
112 remaining -= strlen(path) + 1;
113 RtlStringCbCopyA((PCHAR)(rep + 1) + strlen(path) + 1, remaining, watch_context->token);
115 list_entry = (xenbus_read_queue_item_t *)ExAllocatePoolWithTag(NonPagedPool, sizeof(xenbus_read_queue_item_t), XENPCI_POOL_TAG);
116 list_entry->data = rep;
117 list_entry->length = sizeof(*rep) + rep->len;
118 list_entry->offset = 0;
119 InsertTailList(&xpdid->xenbus.read_list_head, (PLIST_ENTRY)list_entry);
121 status = WdfIoQueueRetrieveNextRequest(xpdid->xenbus.io_queue, &request);
122 if (NT_SUCCESS(status))
123 {
124 WDF_REQUEST_PARAMETERS parameters;
125 WDF_REQUEST_PARAMETERS_INIT(&parameters);
126 WdfRequestGetParameters(request, &parameters);
128 KdPrint((__DRIVER_NAME " found pending read - MinorFunction = %d, length = %d\n", (ULONG)parameters.MinorFunction, (ULONG)parameters.Parameters.Read.Length));
129 XenBus_ProcessReadRequest(xpdid->xenbus.io_queue, request, parameters.Parameters.Read.Length);
130 KeReleaseSpinLock(&xpdid->lock, old_irql);
131 WdfRequestComplete(request, STATUS_SUCCESS);
132 }
133 else
134 {
135 KdPrint((__DRIVER_NAME " no pending read (%08x)\n", status));
136 KeReleaseSpinLock(&xpdid->lock, old_irql);
137 }
139 FUNCTION_EXIT();
140 }
142 static VOID
143 XenBus_EvtFileCleanup(WDFFILEOBJECT file_object)
144 {
145 PXENPCI_DEVICE_INTERFACE_DATA xpdid = GetXpdid(file_object);
146 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfFileObjectGetDevice(file_object));
147 watch_context_t *watch_context;
148 KIRQL old_irql;
149 PCHAR msg;
151 FUNCTION_ENTER();
153 KeAcquireSpinLock(&xpdid->lock, &old_irql);
155 while (!IsListEmpty(&xpdid->xenbus.watch_list_head))
156 {
157 watch_context = (watch_context_t *)RemoveHeadList(&xpdid->xenbus.watch_list_head);
158 KeReleaseSpinLock(&xpdid->lock, old_irql);
159 msg = XenBus_RemWatch(xpdd, XBT_NIL, watch_context->path, XenPci_IoWatch, watch_context);
160 if (msg != NULL)
161 {
162 KdPrint((__DRIVER_NAME " Error freeing watch (%s)\n", msg));
163 XenPci_FreeMem(msg);
164 }
165 ExFreePoolWithTag(watch_context, XENPCI_POOL_TAG);
166 WdfObjectDereference(file_object);
167 KeAcquireSpinLock(&xpdid->lock, &old_irql);
168 }
170 KeReleaseSpinLock(&xpdid->lock, old_irql);
172 FUNCTION_EXIT();
173 }
175 static VOID
176 XenBus_EvtFileClose(WDFFILEOBJECT file_object)
177 {
178 UNREFERENCED_PARAMETER(file_object);
179 FUNCTION_ENTER();
180 FUNCTION_EXIT();
181 }
183 static VOID
184 XenBus_EvtIoRead(WDFQUEUE queue, WDFREQUEST request, size_t length)
185 {
186 NTSTATUS status;
187 WDFFILEOBJECT file_object = WdfRequestGetFileObject(request);
188 PXENPCI_DEVICE_INTERFACE_DATA xpdid = GetXpdid(file_object);
189 KIRQL old_irql;
191 UNREFERENCED_PARAMETER(queue);
193 FUNCTION_ENTER();
194 status = WdfRequestForwardToIoQueue(request, xpdid->xenbus.io_queue);
195 if (!NT_SUCCESS(status))
196 {
197 KdPrint((__DRIVER_NAME " could not forward request (%08x)\n", status));
198 }
199 KeAcquireSpinLock(&xpdid->lock, &old_irql);
200 if (!IsListEmpty(&xpdid->xenbus.read_list_head))
201 {
202 status = WdfIoQueueRetrieveNextRequest(xpdid->xenbus.io_queue, &request);
203 if (NT_SUCCESS(status))
204 {
205 KdPrint((__DRIVER_NAME " found pending read\n"));
206 XenBus_ProcessReadRequest(xpdid->xenbus.io_queue, request, length);
207 KeReleaseSpinLock(&xpdid->lock, old_irql);
208 WdfRequestComplete(request, STATUS_SUCCESS);
209 }
210 else
211 {
212 KdPrint((__DRIVER_NAME " no pending read (%08x)\n", status));
213 KeReleaseSpinLock(&xpdid->lock, old_irql);
214 }
215 }
216 else
217 {
218 KdPrint((__DRIVER_NAME " no data to read\n"));
219 KeReleaseSpinLock(&xpdid->lock, old_irql);
220 }
222 FUNCTION_EXIT();
223 return;
224 }
226 static VOID
227 XenBus_EvtIoWrite(WDFQUEUE queue, WDFREQUEST request, size_t length)
228 {
229 NTSTATUS status;
230 PXENPCI_DEVICE_DATA xpdd = GetXpdd(WdfIoQueueGetDevice(queue));
231 WDFFILEOBJECT file_object = WdfRequestGetFileObject(request);
232 PXENPCI_DEVICE_INTERFACE_DATA xpdid = GetXpdid(file_object);
233 KIRQL old_irql;
234 PUCHAR buffer;
235 PUCHAR src_ptr;
236 ULONG src_len;
237 PUCHAR dst_ptr;
238 ULONG copy_len;
239 struct xsd_sockmsg *rep;
240 xenbus_read_queue_item_t *list_entry;
241 watch_context_t *watch_context;
242 PCHAR watch_path;
243 PCHAR watch_token;
244 PCHAR msg;
246 FUNCTION_ENTER();
248 status = WdfRequestRetrieveInputBuffer(request, length, &buffer, NULL);
249 ASSERT(NT_SUCCESS(status));
251 src_ptr = (PUCHAR)buffer;
252 src_len = (ULONG)length;
253 dst_ptr = xpdid->xenbus.u.buffer + xpdid->xenbus.len;
254 while (src_len != 0)
255 {
256 KdPrint((__DRIVER_NAME " %d bytes of write buffer remaining\n", src_len));
257 /* get a complete msg header */
258 if (xpdid->xenbus.len < sizeof(xpdid->xenbus.u.msg))
259 {
260 copy_len = min(sizeof(xpdid->xenbus.u.msg) - xpdid->xenbus.len, src_len);
261 if (!copy_len)
262 continue;
263 memcpy(dst_ptr, src_ptr, copy_len);
264 dst_ptr += copy_len;
265 src_ptr += copy_len;
266 src_len -= copy_len;
267 xpdid->xenbus.len += copy_len;
268 }
269 /* exit if we can't get that */
270 if (xpdid->xenbus.len < sizeof(xpdid->xenbus.u.msg))
271 continue;
272 /* get a complete msg body */
273 if (xpdid->xenbus.len < sizeof(xpdid->xenbus.u.msg) + xpdid->xenbus.u.msg.len)
274 {
275 copy_len = min(sizeof(xpdid->xenbus.u.msg) + xpdid->xenbus.u.msg.len - xpdid->xenbus.len, src_len);
276 if (!copy_len)
277 continue;
278 memcpy(dst_ptr, src_ptr, copy_len);
279 dst_ptr += copy_len;
280 src_ptr += copy_len;
281 src_len -= copy_len;
282 xpdid->xenbus.len += copy_len;
283 }
284 /* exit if we can't get that */
285 if (xpdid->xenbus.len < sizeof(xpdid->xenbus.u.msg) + xpdid->xenbus.u.msg.len)
286 {
287 continue;
288 }
290 switch (xpdid->xenbus.u.msg.type)
291 {
292 case XS_WATCH:
293 case XS_UNWATCH:
294 KeAcquireSpinLock(&xpdid->lock, &old_irql);
295 watch_context = (watch_context_t *)ExAllocatePoolWithTag(NonPagedPool, sizeof(watch_context_t), XENPCI_POOL_TAG);
296 watch_path = (PCHAR)(xpdid->xenbus.u.buffer + sizeof(struct xsd_sockmsg));
297 watch_token = (PCHAR)(xpdid->xenbus.u.buffer + sizeof(struct xsd_sockmsg) + strlen(watch_path) + 1);
298 RtlStringCbCopyA(watch_context->path, ARRAY_SIZE(watch_context->path), watch_path);
299 RtlStringCbCopyA(watch_context->token, ARRAY_SIZE(watch_context->path), watch_token);
300 watch_context->file_object = file_object;
301 if (xpdid->xenbus.u.msg.type == XS_WATCH)
302 InsertTailList(&xpdid->xenbus.watch_list_head, &watch_context->entry);
303 KeReleaseSpinLock(&xpdid->lock, old_irql);
304 if (xpdid->xenbus.u.msg.type == XS_WATCH)
305 msg = XenBus_AddWatch(xpdd, XBT_NIL, watch_path, XenPci_IoWatch, watch_context);
306 else
307 msg = XenBus_RemWatch(xpdd, XBT_NIL, watch_path, XenPci_IoWatch, watch_context);
308 KeAcquireSpinLock(&xpdid->lock, &old_irql);
309 if (msg != NULL)
310 {
311 rep = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct xsd_sockmsg) + strlen(msg) + 1, XENPCI_POOL_TAG);
312 rep->type = XS_ERROR;
313 rep->req_id = xpdid->xenbus.u.msg.req_id;
314 rep->tx_id = xpdid->xenbus.u.msg.tx_id;
315 rep->len = (ULONG)(strlen(msg) + 0);
316 RtlStringCbCopyA((PCHAR)(rep + 1), strlen(msg) + 1, msg);
317 if (xpdid->xenbus.u.msg.type == XS_WATCH)
318 RemoveEntryList(&watch_context->entry);
319 }
320 else
321 {
322 if (xpdid->xenbus.u.msg.type == XS_WATCH)
323 {
324 WdfObjectReference(file_object);
325 }
326 else
327 {
328 RemoveEntryList(&watch_context->entry);
329 WdfObjectDereference(file_object);
330 }
331 rep = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct xsd_sockmsg), XENPCI_POOL_TAG);
332 rep->type = xpdid->xenbus.u.msg.type;
333 rep->req_id = xpdid->xenbus.u.msg.req_id;
334 rep->tx_id = xpdid->xenbus.u.msg.tx_id;
335 rep->len = 0;
336 }
337 KeReleaseSpinLock(&xpdid->lock, old_irql);
338 break;
339 default:
340 rep = XenBus_Raw(xpdd, &xpdid->xenbus.u.msg);
341 break;
342 }
343 xpdid->xenbus.len = 0;
345 KeAcquireSpinLock(&xpdid->lock, &old_irql);
346 list_entry = (xenbus_read_queue_item_t *)ExAllocatePoolWithTag(NonPagedPool, sizeof(xenbus_read_queue_item_t), XENPCI_POOL_TAG);
347 list_entry->data = rep;
348 list_entry->length = sizeof(*rep) + rep->len;
349 list_entry->offset = 0;
350 InsertTailList(&xpdid->xenbus.read_list_head, (PLIST_ENTRY)list_entry);
351 KeReleaseSpinLock(&xpdid->lock, old_irql);
352 }
353 KdPrint((__DRIVER_NAME " completing request with length %d\n", length));
354 WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, length);
356 FUNCTION_EXIT();
357 }
359 NTSTATUS
360 XenBus_DeviceFileInit(WDFDEVICE device, PWDF_IO_QUEUE_CONFIG queue_config, WDFFILEOBJECT file_object)
361 {
362 NTSTATUS status;
363 PXENPCI_DEVICE_INTERFACE_DATA xpdid = GetXpdid(file_object);
364 WDF_IO_QUEUE_CONFIG internal_queue_config;
366 FUNCTION_ENTER();
368 xpdid->EvtFileCleanup = XenBus_EvtFileCleanup;
369 xpdid->EvtFileClose = XenBus_EvtFileClose;
370 queue_config->EvtIoRead = XenBus_EvtIoRead;
371 queue_config->EvtIoWrite = XenBus_EvtIoWrite;
372 // queue_config->EvtIoDeviceControl = XenBus_EvtIoDeviceControl;
374 InitializeListHead(&xpdid->xenbus.read_list_head);
375 InitializeListHead(&xpdid->xenbus.watch_list_head);
376 xpdid->xenbus.len = 0;
377 WDF_IO_QUEUE_CONFIG_INIT(&internal_queue_config, WdfIoQueueDispatchManual);
379 status = WdfIoQueueCreate(device, &internal_queue_config, WDF_NO_OBJECT_ATTRIBUTES, &xpdid->xenbus.io_queue);
380 if (!NT_SUCCESS(status)) {
381 KdPrint(("Error creating queue 0x%x\n", status));
382 FUNCTION_EXIT();
383 return status;
384 }
386 FUNCTION_EXIT();
388 return status;
389 }