VirtualBox

source: vbox/trunk/src/VBox/VMM/PDMAsyncCompletion.cpp@ 20774

Last change on this file since 20774 was 20187, checked in by vboxsync, 16 years ago

Fix crash in async completion code when an error occurs during init before the PDM async completion module was initialized. Move the initialization before the drivers as they may use the API during init

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.2 KB
Line 
1/* $Id: PDMAsyncCompletion.cpp 20187 2009-06-02 12:39:15Z vboxsync $ */
2/** @file
3 * PDM Async I/O - Transport data asynchronous in R3 using EMT.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION
27#include "PDMInternal.h"
28#include <VBox/pdm.h>
29#include <VBox/mm.h>
30#include <VBox/rem.h>
31#include <VBox/vm.h>
32#include <VBox/err.h>
33
34#include <VBox/log.h>
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/thread.h>
38#include <iprt/mem.h>
39#include <iprt/critsect.h>
40#include <iprt/tcp.h>
41
42#include <VBox/pdmasynccompletion.h>
43#include "PDMAsyncCompletionInternal.h"
44
45/**
46 * Async I/O type.
47 */
48typedef enum PDMASYNCCOMPLETIONTEMPLATETYPE
49{
50 /** Device . */
51 PDMASYNCCOMPLETIONTEMPLATETYPE_DEV = 1,
52 /** Driver consumer. */
53 PDMASYNCCOMPLETIONTEMPLATETYPE_DRV,
54 /** Internal consumer. */
55 PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL,
56 /** Usb consumer. */
57 PDMASYNCCOMPLETIONTEMPLATETYPE_USB
58} PDMASYNCTEMPLATETYPE;
59
60/**
61 * PDM Async I/O template.
62 */
63typedef struct PDMASYNCCOMPLETIONTEMPLATE
64{
65 /** Pointer to the next template in the list. */
66 R3PTRTYPE(PPDMASYNCCOMPLETIONTEMPLATE) pNext;
67 /** Pointer to the previous template in the list. */
68 R3PTRTYPE(PPDMASYNCCOMPLETIONTEMPLATE) pPrev;
69 /** Type specific data. */
70 union
71 {
72 /** PDMASYNCCOMPLETIONTEMPLATETYPE_DEV */
73 struct
74 {
75 /** Pointer to consumer function. */
76 R3PTRTYPE(PFNPDMASYNCCOMPLETEDEV) pfnCompleted;
77 /** Pointer to the device instance owning the template. */
78 R3PTRTYPE(PPDMDEVINS) pDevIns;
79 } Dev;
80 /** PDMASYNCCOMPLETIONTEMPLATETYPE_DRV */
81 struct
82 {
83 /** Pointer to consumer function. */
84 R3PTRTYPE(PFNPDMASYNCCOMPLETEDRV) pfnCompleted;
85 /** Pointer to the driver instance owning the template. */
86 R3PTRTYPE(PPDMDRVINS) pDrvIns;
87 /** User agument given during template creation.
88 * This is only here to make things much easier
89 * for DrVVD. */
90 void *pvTemplateUser;
91 } Drv;
92 /** PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL */
93 struct
94 {
95 /** Pointer to consumer function. */
96 R3PTRTYPE(PFNPDMASYNCCOMPLETEINT) pfnCompleted;
97 /** Pointer to user data. */
98 R3PTRTYPE(void *) pvUser;
99 } Int;
100 /** PDMASYNCCOMPLETIONTEMPLATETYPE_USB */
101 struct
102 {
103 /** Pointer to consumer function. */
104 R3PTRTYPE(PFNPDMASYNCCOMPLETEUSB) pfnCompleted;
105 /** Pointer to the usb instance owning the template. */
106 R3PTRTYPE(PPDMUSBINS) pUsbIns;
107 } Usb;
108 } u;
109 /** Template type. */
110 PDMASYNCCOMPLETIONTEMPLATETYPE enmType;
111 /** Pointer to the VM. */
112 R3PTRTYPE(PVM) pVM;
113 /** Use count of the template. */
114 volatile uint32_t cUsed;
115} PDMASYNCCOMPLETIONTEMPLATE;
116
117static void pdmR3AsyncCompletionPutTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, PPDMASYNCCOMPLETIONTASK pTask, bool fLocal);
118
119/**
120 * Internal worker for the creation apis
121 *
122 * @returns VBox status.
123 * @param pVM VM handle.
124 * @param ppTemplate Where to store the template handle.
125 */
126static int pdmR3AsyncCompletionTemplateCreate(PVM pVM, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE enmType)
127{
128 int rc = VINF_SUCCESS;
129
130 if (ppTemplate == NULL)
131 {
132 AssertMsgFailed(("ppTemplate is NULL\n"));
133 return VERR_INVALID_PARAMETER;
134 }
135
136 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
137 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMASYNCCOMPLETIONTEMPLATE), (void **)&pTemplate);
138 if (RT_FAILURE(rc))
139 return rc;
140
141 /*
142 * Initialize fields.
143 */
144 pTemplate->pVM = pVM;
145 pTemplate->cUsed = 0;
146 pTemplate->enmType = enmType;
147
148 /*
149 * Add template to the global VM template list.
150 */
151 pTemplate->pNext = pVM->pdm.s.pAsyncCompletionTemplates;
152 if (pVM->pdm.s.pAsyncCompletionTemplates)
153 pVM->pdm.s.pAsyncCompletionTemplates->pPrev = pTemplate;
154 pVM->pdm.s.pAsyncCompletionTemplates = pTemplate;
155
156 *ppTemplate = pTemplate;
157 return VINF_SUCCESS;
158}
159
160/**
161 * Creates a async completion template for a device instance.
162 *
163 * The template is used when creating new completion tasks.
164 *
165 * @returns VBox status code.
166 * @param pVM Pointer to the shared VM structure.
167 * @param pDevIns The device instance.
168 * @param ppTemplate Where to store the template pointer on success.
169 * @param pfnCompleted The completion callback routine.
170 * @param pszDesc Description.
171 */
172VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateDevice(PVM pVM, PPDMDEVINS pDevIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEDEV pfnCompleted, const char *pszDesc)
173{
174 LogFlow(("%s: pDevIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n",
175 __FUNCTION__, pDevIns, ppTemplate, pfnCompleted, pszDesc));
176
177 /*
178 * Validate input.
179 */
180 VM_ASSERT_EMT(pVM);
181 if (!pfnCompleted)
182 {
183 AssertMsgFailed(("No completion callback!\n"));
184 return VERR_INVALID_PARAMETER;
185 }
186
187 if (!ppTemplate)
188 {
189 AssertMsgFailed(("Template pointer is NULL!\n"));
190 return VERR_INVALID_PARAMETER;
191 }
192
193 /*
194 * Create the template.
195 */
196 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
197 int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_DEV);
198 if (RT_SUCCESS(rc))
199 {
200 pTemplate->u.Dev.pDevIns = pDevIns;
201 pTemplate->u.Dev.pfnCompleted = pfnCompleted;
202
203 *ppTemplate = pTemplate;
204 Log(("PDM: Created device template %p: pfnCompleted=%p pDevIns=%p\n",
205 pTemplate, pfnCompleted, pDevIns));
206 }
207
208 return rc;
209}
210
211/**
212 * Creates a async completion template for a driver instance.
213 *
214 * The template is used when creating new completion tasks.
215 *
216 * @returns VBox status code.
217 * @param pVM Pointer to the shared VM structure.
218 * @param pDrvIns The driver instance.
219 * @param ppTemplate Where to store the template pointer on success.
220 * @param pfnCompleted The completion callback routine.
221 * @param pvTemplateUser Template user argument
222 * @param pszDesc Description.
223 */
224VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEDRV pfnCompleted, void *pvTemplateUser, const char *pszDesc)
225{
226 LogFlow(("%s: pDrvIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n",
227 __FUNCTION__, pDrvIns, ppTemplate, pfnCompleted, pszDesc));
228
229 /*
230 * Validate input.
231 */
232 VM_ASSERT_EMT(pVM);
233 if (!pfnCompleted)
234 {
235 AssertMsgFailed(("No completion callback!\n"));
236 return VERR_INVALID_PARAMETER;
237 }
238
239 if (!ppTemplate)
240 {
241 AssertMsgFailed(("Template pointer is NULL!\n"));
242 return VERR_INVALID_PARAMETER;
243 }
244
245 /*
246 * Create the template.
247 */
248 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
249 int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_DRV);
250 if (RT_SUCCESS(rc))
251 {
252 pTemplate->u.Drv.pDrvIns = pDrvIns;
253 pTemplate->u.Drv.pfnCompleted = pfnCompleted;
254 pTemplate->u.Drv.pvTemplateUser = pvTemplateUser;
255
256 *ppTemplate = pTemplate;
257 Log(("PDM: Created driver template %p: pfnCompleted=%p pDrvIns=%p\n",
258 pTemplate, pfnCompleted, pDrvIns));
259 }
260
261 return rc;
262}
263
264/**
265 * Creates a async completion template for a USB device instance.
266 *
267 * The template is used when creating new completion tasks.
268 *
269 * @returns VBox status code.
270 * @param pVM Pointer to the shared VM structure.
271 * @param pUsbIns The USB device instance.
272 * @param ppTemplate Where to store the template pointer on success.
273 * @param pfnCompleted The completion callback routine.
274 * @param pszDesc Description.
275 */
276VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateUsb(PVM pVM, PPDMUSBINS pUsbIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEUSB pfnCompleted, const char *pszDesc)
277{
278 LogFlow(("%s: pUsbIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n",
279 __FUNCTION__, pUsbIns, ppTemplate, pfnCompleted, pszDesc));
280
281 /*
282 * Validate input.
283 */
284 VM_ASSERT_EMT(pVM);
285 if (!pfnCompleted)
286 {
287 AssertMsgFailed(("No completion callback!\n"));
288 return VERR_INVALID_PARAMETER;
289 }
290
291 if (!ppTemplate)
292 {
293 AssertMsgFailed(("Template pointer is NULL!\n"));
294 return VERR_INVALID_PARAMETER;
295 }
296
297 /*
298 * Create the template.
299 */
300 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
301 int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_USB);
302 if (RT_SUCCESS(rc))
303 {
304 pTemplate->u.Usb.pUsbIns = pUsbIns;
305 pTemplate->u.Usb.pfnCompleted = pfnCompleted;
306
307 *ppTemplate = pTemplate;
308 Log(("PDM: Created usb template %p: pfnCompleted=%p pDevIns=%p\n",
309 pTemplate, pfnCompleted, pUsbIns));
310 }
311
312 return rc;
313}
314
315/**
316 * Creates a async completion template for internally by the VMM.
317 *
318 * The template is used when creating new completion tasks.
319 *
320 * @returns VBox status code.
321 * @param pVM Pointer to the shared VM structure.
322 * @param ppTemplate Where to store the template pointer on success.
323 * @param pfnCompleted The completion callback routine.
324 * @param pvUser2 The 2nd user argument for the callback.
325 * @param pszDesc Description.
326 */
327VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateInternal(PVM pVM, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEINT pfnCompleted, void *pvUser2, const char *pszDesc)
328{
329 LogFlow(("%s: ppTemplate=%p pfnCompleted=%p pvUser2=%p pszDesc=%s\n",
330 __FUNCTION__, ppTemplate, pfnCompleted, pvUser2, pszDesc));
331
332 /*
333 * Validate input.
334 */
335 VM_ASSERT_EMT(pVM);
336 if (!pfnCompleted)
337 {
338 AssertMsgFailed(("No completion callback!\n"));
339 return VERR_INVALID_PARAMETER;
340 }
341
342 if (!ppTemplate)
343 {
344 AssertMsgFailed(("Template pointer is NULL!\n"));
345 return VERR_INVALID_PARAMETER;
346 }
347
348 /*
349 * Create the template.
350 */
351 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
352 int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL);
353 if (RT_SUCCESS(rc))
354 {
355 pTemplate->u.Int.pvUser = pvUser2;
356 pTemplate->u.Int.pfnCompleted = pfnCompleted;
357
358 *ppTemplate = pTemplate;
359 Log(("PDM: Created internal template %p: pfnCompleted=%p pvUser2=%p\n",
360 pTemplate, pfnCompleted, pvUser2));
361 }
362
363 return rc;
364}
365
366/**
367 * Destroys the specified async completion template.
368 *
369 * @returns VBox status codes:
370 * @retval VINF_SUCCESS on success.
371 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if the template is still in use.
372 *
373 * @param pTemplate The template in question.
374 */
375VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroy(PPDMASYNCCOMPLETIONTEMPLATE pTemplate)
376{
377 LogFlow(("%s: pTemplate=%p\n", __FUNCTION__, pTemplate));
378
379 if (!pTemplate)
380 {
381 AssertMsgFailed(("pTemplate is NULL!\n"));
382 return VERR_INVALID_PARAMETER;
383 }
384
385 /*
386 * Check if the template is still used.
387 */
388 if (pTemplate->cUsed > 0)
389 {
390 AssertMsgFailed(("Template is still in use\n"));
391 return VERR_PDM_ASYNC_TEMPLATE_BUSY;
392 }
393
394 /*
395 * Unlink the template from the list.
396 */
397 PVM pVM = pTemplate->pVM;
398 PPDMASYNCCOMPLETIONTEMPLATE pPrev = pTemplate->pPrev;
399 PPDMASYNCCOMPLETIONTEMPLATE pNext = pTemplate->pNext;
400
401 if (pPrev)
402 pPrev->pNext = pNext;
403 else
404 pVM->pdm.s.pAsyncCompletionTemplates = pNext;
405
406 if (pNext)
407 pNext->pPrev = pPrev;
408
409 /*
410 * Free the template.
411 */
412 MMR3HeapFree(pTemplate);
413
414 return VINF_SUCCESS;
415}
416
417/**
418 * Destroys all the specified async completion templates for the given device instance.
419 *
420 * @returns VBox status codes:
421 * @retval VINF_SUCCESS on success.
422 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
423 *
424 * @param pVM Pointer to the shared VM structure.
425 * @param pDevIns The device instance.
426 */
427VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
428{
429 LogFlow(("%s: pDevIns=%p\n", __FUNCTION__, pDevIns));
430
431 /*
432 * Validate input.
433 */
434 if (!pDevIns)
435 return VERR_INVALID_PARAMETER;
436 VM_ASSERT_EMT(pVM);
437
438 /*
439 * Unlink it.
440 */
441 PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pVM->pdm.s.pAsyncCompletionTemplates;
442 while (pTemplate)
443 {
444 if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_DEV
445 && pTemplate->u.Dev.pDevIns == pDevIns)
446 {
447 PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate;
448 pTemplate = pTemplate->pNext;
449 int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy);
450 if (RT_FAILURE(rc))
451 return rc;
452 }
453 else
454 pTemplate = pTemplate->pNext;
455 }
456
457 return VINF_SUCCESS;
458}
459
460/**
461 * Destroys all the specified async completion templates for the given driver instance.
462 *
463 * @returns VBox status codes:
464 * @retval VINF_SUCCESS on success.
465 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
466 *
467 * @param pVM Pointer to the shared VM structure.
468 * @param pDrvIns The driver instance.
469 */
470VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
471{
472 LogFlow(("%s: pDevIns=%p\n", __FUNCTION__, pDrvIns));
473
474 /*
475 * Validate input.
476 */
477 if (!pDrvIns)
478 return VERR_INVALID_PARAMETER;
479 VM_ASSERT_EMT(pVM);
480
481 /*
482 * Unlink it.
483 */
484 PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pVM->pdm.s.pAsyncCompletionTemplates;
485 while (pTemplate)
486 {
487 if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_DRV
488 && pTemplate->u.Drv.pDrvIns == pDrvIns)
489 {
490 PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate;
491 pTemplate = pTemplate->pNext;
492 int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy);
493 if (RT_FAILURE(rc))
494 return rc;
495 }
496 else
497 pTemplate = pTemplate->pNext;
498 }
499
500 return VINF_SUCCESS;
501}
502
503/**
504 * Destroys all the specified async completion templates for the given USB device instance.
505 *
506 * @returns VBox status codes:
507 * @retval VINF_SUCCESS on success.
508 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
509 *
510 * @param pVM Pointer to the shared VM structure.
511 * @param pUsbIns The USB device instance.
512 */
513VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyUsb(PVM pVM, PPDMUSBINS pUsbIns)
514{
515 LogFlow(("%s: pUsbIns=%p\n", __FUNCTION__, pUsbIns));
516
517 /*
518 * Validate input.
519 */
520 if (!pUsbIns)
521 return VERR_INVALID_PARAMETER;
522 VM_ASSERT_EMT(pVM);
523
524 /*
525 * Unlink it.
526 */
527 PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pVM->pdm.s.pAsyncCompletionTemplates;
528 while (pTemplate)
529 {
530 if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_USB
531 && pTemplate->u.Usb.pUsbIns == pUsbIns)
532 {
533 PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate;
534 pTemplate = pTemplate->pNext;
535 int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy);
536 if (RT_FAILURE(rc))
537 return rc;
538 }
539 else
540 pTemplate = pTemplate->pNext;
541 }
542
543 return VINF_SUCCESS;
544}
545
546void pdmR3AsyncCompletionCompleteTask(PPDMASYNCCOMPLETIONTASK pTask)
547{
548 LogFlow(("%s: pTask=%p\n", __FUNCTION__, pTask));
549
550 PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pTask->pEndpoint->pTemplate;
551
552 switch (pTemplate->enmType)
553 {
554 case PDMASYNCCOMPLETIONTEMPLATETYPE_DEV:
555 {
556 pTemplate->u.Dev.pfnCompleted(pTemplate->u.Dev.pDevIns, pTask->pvUser);
557 break;
558 }
559 case PDMASYNCCOMPLETIONTEMPLATETYPE_DRV:
560 {
561 pTemplate->u.Drv.pfnCompleted(pTemplate->u.Drv.pDrvIns, pTemplate->u.Drv.pvTemplateUser, pTask->pvUser);
562 break;
563 }
564 case PDMASYNCCOMPLETIONTEMPLATETYPE_USB:
565 {
566 pTemplate->u.Usb.pfnCompleted(pTemplate->u.Usb.pUsbIns, pTask->pvUser);
567 break;
568 }
569 case PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL:
570 {
571 pTemplate->u.Int.pfnCompleted(pTemplate->pVM, pTask->pvUser, pTemplate->u.Int.pvUser);
572 break;
573 }
574 default:
575 AssertMsgFailed(("Unknown template type!\n"));
576 }
577
578 pdmR3AsyncCompletionPutTask(pTask->pEndpoint, pTask, true);
579}
580
581/**
582 * Worker initializing a endpoint class.
583 *
584 * @returns VBox statis code.
585 * @param pVM Pointer to the shared VM instance data.
586 * @param pEpClass Pointer to the endpoint class structure.
587 * @param pCfgHandle Pointer to the the CFGM tree.
588 */
589int pdmR3AsyncCompletionEpClassInit(PVM pVM, PCPDMASYNCCOMPLETIONEPCLASSOPS pEpClassOps, PCFGMNODE pCfgHandle)
590{
591 int rc = VINF_SUCCESS;
592
593 /* Validate input. */
594 if ( !pEpClassOps
595 || (pEpClassOps->u32Version != PDMAC_EPCLASS_OPS_VERSION)
596 || (pEpClassOps->u32VersionEnd != PDMAC_EPCLASS_OPS_VERSION))
597 AssertMsgFailedReturn(("Invalid endpoint class data\n"), VERR_VERSION_MISMATCH);
598
599 LogFlowFunc((": pVM=%p pEpClassOps=%p{%s}\n", pVM, pEpClassOps, pEpClassOps->pcszName));
600
601 /* Allocate global class data. */
602 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = NULL;
603
604 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
605 pEpClassOps->cbEndpointClassGlobal,
606 (void **)&pEndpointClass);
607 if (RT_SUCCESS(rc))
608 {
609 /* Initialize common data. */
610 pEndpointClass->pVM = pVM;
611 pEndpointClass->pEndpointOps = pEpClassOps;
612
613 rc = RTCritSectInit(&pEndpointClass->CritSect);
614 if (RT_SUCCESS(rc))
615 {
616 PCFGMNODE pCfgNodeClass = CFGMR3GetChild(pCfgHandle, pEpClassOps->pcszName);
617
618 /* Query the common CFGM options */
619 rc = CFGMR3QueryU32Def(pCfgNodeClass, "TaskCachePerEndpoint", &pEndpointClass->cEndpointCacheSize, 5);
620 AssertRCReturn(rc, rc);
621
622 rc = CFGMR3QueryU32Def(pCfgNodeClass, "TaskCachePerClass", &pEndpointClass->cEpClassCacheSize, 50);
623 AssertRCReturn(rc, rc);
624
625 /* Call the specific endpoint class initializer. */
626 rc = pEpClassOps->pfnInitialize(pEndpointClass, pCfgNodeClass);
627 if (RT_SUCCESS(rc))
628 {
629 AssertMsg(!pVM->pdm.s.papAsyncCompletionEndpointClass[pEpClassOps->enmClassType],
630 ("Endpoint class was already initialized\n"));
631
632 pVM->pdm.s.papAsyncCompletionEndpointClass[pEpClassOps->enmClassType] = pEndpointClass;
633 LogFlowFunc((": Initialized endpoint class \"%s\" rc=%Rrc\n", pEpClassOps->pcszName, rc));
634 return VINF_SUCCESS;
635 }
636 RTCritSectDelete(&pEndpointClass->CritSect);
637 }
638 MMR3HeapFree(pEndpointClass);
639 }
640
641 LogFlowFunc((": Failed to initialize endpoint class rc=%Rrc\n", rc));
642
643 return rc;
644}
645
646/**
647 * Worker terminating all endpoint classes.
648 *
649 * @returns nothing
650 * @param pEndpointClass Pointer to the endpoint class to terminate.
651 *
652 * @remarks This method ensures that any still open endpoint is closed.
653 */
654static void pdmR3AsyncCompletionEpClassTerminate(PPDMASYNCCOMPLETIONEPCLASS pEndpointClass)
655{
656 int rc = VINF_SUCCESS;
657 PVM pVM = pEndpointClass->pVM;
658
659 /* Close all still open endpoints. */
660 while (pEndpointClass->pEndpointsHead)
661 PDMR3AsyncCompletionEpClose(pEndpointClass->pEndpointsHead);
662
663 /* Destroy all cached tasks. */
664 for (unsigned i = 0; i < RT_ELEMENTS(pEndpointClass->apTaskCache); i++)
665 {
666 PPDMASYNCCOMPLETIONTASK pTask = pEndpointClass->apTaskCache[i];
667
668 while (pTask)
669 {
670 PPDMASYNCCOMPLETIONTASK pTaskFree = pTask;
671 pTask = pTask->pNext;
672 MMR3HeapFree(pTaskFree);
673 }
674 }
675
676 /* Call the termination callback of the class. */
677 pEndpointClass->pEndpointOps->pfnTerminate(pEndpointClass);
678
679 RTCritSectDelete(&pEndpointClass->CritSect);
680
681 /* Free the memory of the class finally and clear the entry in the class array. */
682 pVM->pdm.s.papAsyncCompletionEndpointClass[pEndpointClass->pEndpointOps->enmClassType] = NULL;
683 MMR3HeapFree(pEndpointClass);
684}
685
686/**
687 * Initialize the async completion manager.
688 *
689 * @returns VBox status code
690 * @param pVM Pointer to the shared VM structure.
691 */
692int pdmR3AsyncCompletionInit(PVM pVM)
693{
694 int rc = VINF_SUCCESS;
695
696 LogFlowFunc((": pVM=%p\n", pVM));
697
698 VM_ASSERT_EMT(pVM);
699
700 do
701 {
702 /* Allocate array for global class data. */
703 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
704 sizeof(PPDMASYNCCOMPLETIONEPCLASS) * PDMASYNCCOMPLETIONEPCLASSTYPE_MAX,
705 (void **)&pVM->pdm.s.papAsyncCompletionEndpointClass);
706 if (RT_FAILURE(rc))
707 break;
708
709 PCFGMNODE pCfgRoot = CFGMR3GetRoot(pVM);
710 PCFGMNODE pCfgAsyncCompletion = CFGMR3GetChild(CFGMR3GetChild(pCfgRoot, "PDM"), "AsyncCompletion");
711
712 rc = pdmR3AsyncCompletionEpClassInit(pVM, &g_PDMAsyncCompletionEndpointClassFile, pCfgAsyncCompletion);
713 if (RT_FAILURE(rc))
714 break;
715
716 /* Put other classes here. */
717 } while (0);
718
719 LogFlowFunc((": pVM=%p rc=%Rrc\n", pVM, rc));
720
721 return rc;
722}
723
724/**
725 * Terminates the async completion manager.
726 *
727 * @returns VBox status code
728 * @param pVM Pointer to the shared VM structure.
729 */
730int pdmR3AsyncCompletionTerm(PVM pVM)
731{
732 LogFlowFunc((": pVM=%p\n", pVM));
733
734 if (pVM->pdm.s.papAsyncCompletionEndpointClass)
735 {
736 pdmR3AsyncCompletionEpClassTerminate(pVM->pdm.s.papAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE]);
737 MMR3HeapFree(pVM->pdm.s.papAsyncCompletionEndpointClass);
738 }
739 return VINF_SUCCESS;
740}
741
742/**
743 * Tries to get a free task from the endpoint or class cache
744 * allocating the task if it fails.
745 *
746 * @returns Pointer to a new and initialized task or NULL
747 * @param pEndpoint The endpoint the task is for.
748 * @param pvUser Opaque user data for the task.
749 */
750static PPDMASYNCCOMPLETIONTASK pdmR3AsyncCompletionGetTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, void *pvUser)
751{
752 PPDMASYNCCOMPLETIONTASK pTask = NULL;
753
754 /* Try the small per endpoint cache first. */
755 if (pEndpoint->pTasksFreeHead == pEndpoint->pTasksFreeTail)
756 {
757 /* Try the bigger per endpoint class cache. */
758 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
759
760 /* We start with the assigned slot id to distribute the load when allocating new tasks. */
761 unsigned iSlot = pEndpoint->iSlotStart;
762 do
763 {
764 pTask = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
765 if (pTask)
766 break;
767
768 iSlot = (iSlot + 1) % RT_ELEMENTS(pEndpointClass->apTaskCache);
769 } while (iSlot != pEndpoint->iSlotStart);
770
771 if (!pTask)
772 {
773 /*
774 * Allocate completely new.
775 * If this fails we return NULL.
776 */
777 int rc = MMR3HeapAllocZEx(pEndpointClass->pVM, MM_TAG_PDM_ASYNC_COMPLETION,
778 pEndpointClass->pEndpointOps->cbTask,
779 (void **)&pTask);
780 if (RT_FAILURE(rc))
781 pTask = NULL;
782 }
783 else
784 {
785 /* Remove the first element and put the rest into the slot again. */
786 PPDMASYNCCOMPLETIONTASK pTaskHeadNew = pTask->pNext;
787
788 /* Put back into the list adding any new tasks. */
789 while (true)
790 {
791 bool fChanged = ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], pTaskHeadNew, NULL);
792
793 if (fChanged)
794 break;
795
796 PPDMASYNCCOMPLETIONTASK pTaskHead = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
797
798 /* The new task could be taken inbetween */
799 if (pTaskHead)
800 {
801 /* Go to the end of the probably much shorter new list. */
802 PPDMASYNCCOMPLETIONTASK pTaskTail = pTaskHead;
803 while (pTaskTail->pNext)
804 pTaskTail = pTaskTail->pNext;
805
806 /* Concatenate */
807 pTaskTail->pNext = pTaskHeadNew;
808
809 pTaskHeadNew = pTaskHead;
810 }
811 /* Another round trying to change the list. */
812 }
813 /* We got a task from the global cache so decrement the counter */
814 ASMAtomicDecU32(&pEndpointClass->cTasksCached);
815 }
816 }
817 else
818 {
819 /* Grab a free task from the head. */
820 AssertMsg(pEndpoint->cTasksCached > 0, ("No tasks cached but list contain more than one element\n"));
821
822 pTask = pEndpoint->pTasksFreeHead;
823 pEndpoint->pTasksFreeHead = pTask->pNext;
824 ASMAtomicDecU32(&pEndpoint->cTasksCached);
825 }
826
827 if (RT_LIKELY(pTask))
828 {
829 /* Get ID of the task. */
830 pTask->uTaskId = ASMAtomicIncU32(&pEndpoint->uTaskIdNext);
831
832 /* Initialize common parts. */
833 pTask->pvUser = pvUser;
834 pTask->pEndpoint = pEndpoint;
835 /* Clear list pointers for safety. */
836 pTask->pPrev = NULL;
837 pTask->pNext = NULL;
838 }
839
840 return pTask;
841}
842
843/**
844 * Puts a task in one of the caches.
845 *
846 * @returns nothing.
847 * @param pEndpoint The endpoint the task belongs to.
848 * @param pTask The task to cache.
849 * @param fLocal Whether the per endpoint cache should be tried first.
850 */
851static void pdmR3AsyncCompletionPutTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, PPDMASYNCCOMPLETIONTASK pTask, bool fLocal)
852{
853 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
854
855 /* Check whether we can use the per endpoint cache */
856 if ( fLocal
857 && (pEndpoint->cTasksCached < pEndpointClass->cEndpointCacheSize))
858 {
859 /* Add it to the list. */
860 pTask->pPrev = NULL;
861 pEndpoint->pTasksFreeTail->pNext = pTask;
862 pEndpoint->pTasksFreeTail = pTask;
863 ASMAtomicIncU32(&pEndpoint->cTasksCached);
864 }
865 else if (ASMAtomicReadU32(&pEndpoint->cTasksCached) < pEndpointClass->cEpClassCacheSize)
866 {
867 /* Use the global cache. */
868 ASMAtomicIncU32(&pEndpointClass->cTasksCached);
869
870 PPDMASYNCCOMPLETIONTASK pNext;
871 do
872 {
873 pNext = pEndpointClass->apTaskCache[pEndpoint->iSlotStart];
874 pTask->pNext = pNext;
875 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[pEndpoint->iSlotStart], (void *)pTask, (void *)pNext));
876 }
877 else
878 {
879 /* Free it */
880 MMR3HeapFree(pTask);
881 }
882}
883
884VMMR3DECL(int) PDMR3AsyncCompletionEpCreateForFile(PPPDMASYNCCOMPLETIONENDPOINT ppEndpoint,
885 const char *pszFilename, uint32_t fFlags,
886 PPDMASYNCCOMPLETIONTEMPLATE pTemplate)
887{
888 int rc = VINF_SUCCESS;
889
890 LogFlowFunc((": ppEndpoint=%p pszFilename=%p{%s} fFlags=%u pTemplate=%p\n",
891 ppEndpoint, pszFilename, pszFilename, fFlags, pTemplate));
892
893 /* Sanity checks. */
894 AssertReturn(VALID_PTR(ppEndpoint), VERR_INVALID_POINTER);
895 AssertReturn(VALID_PTR(pszFilename), VERR_INVALID_POINTER);
896 AssertReturn(VALID_PTR(pTemplate), VERR_INVALID_POINTER);
897
898 /* Check that the flags are valid. */
899 AssertReturn(((~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING) & fFlags) == 0),
900 VERR_INVALID_PARAMETER);
901
902 PVM pVM = pTemplate->pVM;
903 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pVM->pdm.s.papAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
904 PPDMASYNCCOMPLETIONENDPOINT pEndpoint = NULL;
905
906 AssertMsg(pEndpointClass, ("File endpoint class was not initialized\n"));
907
908 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
909 pEndpointClass->pEndpointOps->cbEndpoint,
910 (void **)&pEndpoint);
911 if (RT_SUCCESS(rc))
912 {
913
914 /* Initialize common parts. */
915 pEndpoint->pNext = NULL;
916 pEndpoint->pPrev = NULL;
917 pEndpoint->pEpClass = pEndpointClass;
918 pEndpoint->pTasksFreeHead = NULL;
919 pEndpoint->pTasksFreeTail = NULL;
920 pEndpoint->cTasksCached = 0;
921 pEndpoint->uTaskIdNext = 0;
922 pEndpoint->fTaskIdWraparound = false;
923 pEndpoint->pTemplate = pTemplate;
924 pEndpoint->iSlotStart = pEndpointClass->cEndpoints % RT_ELEMENTS(pEndpointClass->apTaskCache);
925
926 /* Init the cache. */
927 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
928 pEndpointClass->pEndpointOps->cbTask,
929 (void **)&pEndpoint->pTasksFreeHead);
930 if (RT_SUCCESS(rc))
931 {
932 pEndpoint->pTasksFreeTail = pEndpoint->pTasksFreeHead;
933
934 /* Call the initializer for the endpoint. */
935 rc = pEndpointClass->pEndpointOps->pfnEpInitialize(pEndpoint, pszFilename, fFlags);
936 if (RT_SUCCESS(rc))
937 {
938 /* Link it into the list of endpoints. */
939 rc = RTCritSectEnter(&pEndpointClass->CritSect);
940 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
941
942 pEndpoint->pNext = pEndpointClass->pEndpointsHead;
943 if (pEndpointClass->pEndpointsHead)
944 pEndpointClass->pEndpointsHead->pPrev = pEndpoint;
945
946 pEndpointClass->pEndpointsHead = pEndpoint;
947 pEndpointClass->cEndpoints++;
948
949 rc = RTCritSectLeave(&pEndpointClass->CritSect);
950 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
951
952 /* Reference the template. */
953 ASMAtomicIncU32(&pTemplate->cUsed);
954
955 *ppEndpoint = pEndpoint;
956
957 LogFlowFunc((": Created endpoint for %s: rc=%Rrc\n", pszFilename, rc));
958 return VINF_SUCCESS;
959 }
960 MMR3HeapFree(pEndpoint->pTasksFreeHead);
961 }
962 MMR3HeapFree(pEndpoint);
963 }
964
965 LogFlowFunc((": Creation of endpoint for %s failed: rc=%Rrc\n", pszFilename, rc));
966
967 return rc;
968}
969
970VMMR3DECL(void) PDMR3AsyncCompletionEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
971{
972 LogFlowFunc((": pEndpoint=%p\n", pEndpoint));
973
974 /* Sanity checks. */
975 AssertReturnVoid(VALID_PTR(pEndpoint));
976
977 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
978 pEndpointClass->pEndpointOps->pfnEpClose(pEndpoint);
979
980 /* Free cached tasks. */
981 PPDMASYNCCOMPLETIONTASK pTask = pEndpoint->pTasksFreeHead;
982
983 while (pTask)
984 {
985 PPDMASYNCCOMPLETIONTASK pTaskFree = pTask;
986 pTask = pTask->pNext;
987 MMR3HeapFree(pTaskFree);
988 }
989
990 /* Drop reference from the template. */
991 ASMAtomicDecU32(&pEndpoint->pTemplate->cUsed);
992
993 /* Unlink the endpoint from the list. */
994 int rc = RTCritSectEnter(&pEndpointClass->CritSect);
995 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
996
997 PPDMASYNCCOMPLETIONENDPOINT pEndpointNext = pEndpoint->pNext;
998 PPDMASYNCCOMPLETIONENDPOINT pEndpointPrev = pEndpoint->pPrev;
999
1000 if (pEndpointPrev)
1001 pEndpointPrev->pNext = pEndpointNext;
1002 else
1003 pEndpointClass->pEndpointsHead = pEndpointNext;
1004 if (pEndpointNext)
1005 pEndpointNext->pPrev = pEndpointPrev;
1006
1007 pEndpointClass->cEndpoints--;
1008
1009 rc = RTCritSectLeave(&pEndpointClass->CritSect);
1010 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
1011
1012 MMR3HeapFree(pEndpoint);
1013}
1014
1015VMMR3DECL(int) PDMR3AsyncCompletionEpRead(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1016 PCPDMDATASEG paSegments, size_t cSegments,
1017 size_t cbRead, void *pvUser,
1018 PPPDMASYNCCOMPLETIONTASK ppTask)
1019{
1020 int rc = VINF_SUCCESS;
1021
1022 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1023 AssertReturn(VALID_PTR(paSegments), VERR_INVALID_POINTER);
1024 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1025 AssertReturn(cSegments > 0, VERR_INVALID_PARAMETER);
1026 AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
1027 AssertReturn(off >= 0, VERR_INVALID_PARAMETER);
1028
1029 PPDMASYNCCOMPLETIONTASK pTask;
1030
1031 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1032 if (!pTask)
1033 return VERR_NO_MEMORY;
1034
1035 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpRead(pTask, pEndpoint, off,
1036 paSegments, cSegments, cbRead);
1037 if (RT_SUCCESS(rc))
1038 {
1039 *ppTask = pTask;
1040 }
1041 else
1042 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1043
1044 return rc;
1045}
1046
1047VMMR3DECL(int) PDMR3AsyncCompletionEpWrite(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1048 PCPDMDATASEG paSegments, size_t cSegments,
1049 size_t cbWrite, void *pvUser,
1050 PPPDMASYNCCOMPLETIONTASK ppTask)
1051{
1052 int rc = VINF_SUCCESS;
1053
1054 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1055 AssertReturn(VALID_PTR(paSegments), VERR_INVALID_POINTER);
1056 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1057 AssertReturn(cSegments > 0, VERR_INVALID_PARAMETER);
1058 AssertReturn(cbWrite > 0, VERR_INVALID_PARAMETER);
1059 AssertReturn(off >= 0, VERR_INVALID_PARAMETER);
1060
1061 PPDMASYNCCOMPLETIONTASK pTask;
1062
1063 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1064 if (!pTask)
1065 return VERR_NO_MEMORY;
1066
1067 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpWrite(pTask, pEndpoint, off,
1068 paSegments, cSegments, cbWrite);
1069 if (RT_SUCCESS(rc))
1070 {
1071 *ppTask = pTask;
1072 }
1073 else
1074 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1075
1076 return rc;
1077}
1078
1079VMMR3DECL(int) PDMR3AsyncCompletionEpFlush(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
1080 void *pvUser,
1081 PPPDMASYNCCOMPLETIONTASK ppTask)
1082{
1083 int rc = VINF_SUCCESS;
1084
1085 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1086 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1087
1088 PPDMASYNCCOMPLETIONTASK pTask;
1089
1090 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1091 if (!pTask)
1092 return VERR_NO_MEMORY;
1093
1094 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpFlush(pTask, pEndpoint);
1095 if (RT_SUCCESS(rc))
1096 {
1097 *ppTask = pTask;
1098 }
1099 else
1100 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1101
1102 return rc;
1103}
1104
1105VMMR3DECL(int) PDMR3AsyncCompletionEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
1106 uint64_t *pcbSize)
1107{
1108 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1109 AssertReturn(VALID_PTR(pcbSize), VERR_INVALID_POINTER);
1110
1111 return pEndpoint->pEpClass->pEndpointOps->pfnEpGetSize(pEndpoint, pcbSize);
1112}
1113
1114VMMR3DECL(int) PDMR3AsyncCompletionTaskCancel(PPDMASYNCCOMPLETIONTASK pTask)
1115{
1116 return VERR_NOT_IMPLEMENTED;
1117}
1118
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette