VirtualBox

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

Last change on this file since 22544 was 22309, checked in by vboxsync, 15 years ago

PDMAsyncCompletion: Add first part of the cache for file I/O

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.9 KB
Line 
1/* $Id: PDMAsyncCompletion.cpp 22309 2009-08-17 20:59:28Z 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 uint32_t cTasksCached = ASMAtomicReadU32(&pEndpoint->cTasksCached);
756 if (cTasksCached == 0)
757 {
758 /* Try the bigger per endpoint class cache. */
759 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
760
761 /* We start with the assigned slot id to distribute the load when allocating new tasks. */
762 unsigned iSlot = pEndpoint->iSlotStart;
763 do
764 {
765 pTask = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
766 if (pTask)
767 break;
768
769 iSlot = (iSlot + 1) % RT_ELEMENTS(pEndpointClass->apTaskCache);
770 } while (iSlot != pEndpoint->iSlotStart);
771
772 if (!pTask)
773 {
774 /*
775 * Allocate completely new.
776 * If this fails we return NULL.
777 */
778 int rc = MMR3HeapAllocZEx(pEndpointClass->pVM, MM_TAG_PDM_ASYNC_COMPLETION,
779 pEndpointClass->pEndpointOps->cbTask,
780 (void **)&pTask);
781 if (RT_FAILURE(rc))
782 pTask = NULL;
783 }
784 else
785 {
786 /* Remove the first element and put the rest into the slot again. */
787 PPDMASYNCCOMPLETIONTASK pTaskHeadNew = pTask->pNext;
788
789 /* Put back into the list adding any new tasks. */
790 while (true)
791 {
792 bool fChanged = ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], pTaskHeadNew, NULL);
793
794 if (fChanged)
795 break;
796
797 PPDMASYNCCOMPLETIONTASK pTaskHead = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
798
799 /* The new task could be taken inbetween */
800 if (pTaskHead)
801 {
802 /* Go to the end of the probably much shorter new list. */
803 PPDMASYNCCOMPLETIONTASK pTaskTail = pTaskHead;
804 while (pTaskTail->pNext)
805 pTaskTail = pTaskTail->pNext;
806
807 /* Concatenate */
808 pTaskTail->pNext = pTaskHeadNew;
809
810 pTaskHeadNew = pTaskHead;
811 }
812 /* Another round trying to change the list. */
813 }
814 /* We got a task from the global cache so decrement the counter */
815 ASMAtomicDecU32(&pEndpointClass->cTasksCached);
816 }
817 }
818 else
819 {
820 /* Grab a free task from the head. */
821 AssertMsg(pEndpoint->cTasksCached > 0, ("No tasks cached but list contain more than one element\n"));
822
823 pTask = pEndpoint->pTasksFreeHead;
824 pEndpoint->pTasksFreeHead = pTask->pNext;
825 ASMAtomicDecU32(&pEndpoint->cTasksCached);
826 }
827
828 if (RT_LIKELY(pTask))
829 {
830 /* Get ID of the task. */
831 pTask->uTaskId = ASMAtomicIncU32(&pEndpoint->uTaskIdNext);
832
833 /* Initialize common parts. */
834 pTask->pvUser = pvUser;
835 pTask->pEndpoint = pEndpoint;
836 /* Clear list pointers for safety. */
837 pTask->pPrev = NULL;
838 pTask->pNext = NULL;
839 }
840
841 return pTask;
842}
843
844/**
845 * Puts a task in one of the caches.
846 *
847 * @returns nothing.
848 * @param pEndpoint The endpoint the task belongs to.
849 * @param pTask The task to cache.
850 * @param fLocal Whether the per endpoint cache should be tried first.
851 */
852static void pdmR3AsyncCompletionPutTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, PPDMASYNCCOMPLETIONTASK pTask, bool fLocal)
853{
854 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
855
856 /* Check whether we can use the per endpoint cache */
857 if ( fLocal
858 && (pEndpoint->cTasksCached < pEndpointClass->cEndpointCacheSize))
859 {
860 /* Add it to the list. */
861 pTask->pPrev = NULL;
862 pEndpoint->pTasksFreeTail->pNext = pTask;
863 pEndpoint->pTasksFreeTail = pTask;
864 ASMAtomicIncU32(&pEndpoint->cTasksCached);
865 }
866 else if (ASMAtomicReadU32(&pEndpoint->cTasksCached) < pEndpointClass->cEpClassCacheSize)
867 {
868 /* Use the global cache. */
869 ASMAtomicIncU32(&pEndpointClass->cTasksCached);
870
871 PPDMASYNCCOMPLETIONTASK pNext;
872 do
873 {
874 pNext = pEndpointClass->apTaskCache[pEndpoint->iSlotStart];
875 pTask->pNext = pNext;
876 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[pEndpoint->iSlotStart], (void *)pTask, (void *)pNext));
877 }
878 else
879 {
880 /* Free it */
881 MMR3HeapFree(pTask);
882 }
883}
884
885static PPDMASYNCCOMPLETIONENDPOINT pdmR3AsyncCompletionFindEndpointWithUri(PPDMASYNCCOMPLETIONEPCLASS pEndpointClass,
886 const char *pszUri)
887{
888 PPDMASYNCCOMPLETIONENDPOINT pEndpoint = pEndpointClass->pEndpointsHead;
889
890 while (pEndpoint)
891 {
892 if (!RTStrCmp(pEndpoint->pszUri, pszUri))
893 return pEndpoint;
894
895 pEndpoint = pEndpoint->pNext;
896 }
897
898 return NULL;
899}
900
901VMMR3DECL(int) PDMR3AsyncCompletionEpCreateForFile(PPPDMASYNCCOMPLETIONENDPOINT ppEndpoint,
902 const char *pszFilename, uint32_t fFlags,
903 PPDMASYNCCOMPLETIONTEMPLATE pTemplate)
904{
905 int rc = VINF_SUCCESS;
906
907 LogFlowFunc((": ppEndpoint=%p pszFilename=%p{%s} fFlags=%u pTemplate=%p\n",
908 ppEndpoint, pszFilename, pszFilename, fFlags, pTemplate));
909
910 /* Sanity checks. */
911 AssertReturn(VALID_PTR(ppEndpoint), VERR_INVALID_POINTER);
912 AssertReturn(VALID_PTR(pszFilename), VERR_INVALID_POINTER);
913 AssertReturn(VALID_PTR(pTemplate), VERR_INVALID_POINTER);
914
915 /* Check that the flags are valid. */
916 AssertReturn(((~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING) & fFlags) == 0),
917 VERR_INVALID_PARAMETER);
918
919 PVM pVM = pTemplate->pVM;
920 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pVM->pdm.s.papAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
921 PPDMASYNCCOMPLETIONENDPOINT pEndpoint = NULL;
922
923 AssertMsg(pEndpointClass, ("File endpoint class was not initialized\n"));
924
925 /* Search for a already opened endpoint for this file. */
926 pEndpoint = pdmR3AsyncCompletionFindEndpointWithUri(pEndpointClass, pszFilename);
927
928 if(!pEndpoint)
929 {
930 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
931 pEndpointClass->pEndpointOps->cbEndpoint,
932 (void **)&pEndpoint);
933 if (RT_SUCCESS(rc))
934 {
935
936 /* Initialize common parts. */
937 pEndpoint->pNext = NULL;
938 pEndpoint->pPrev = NULL;
939 pEndpoint->pEpClass = pEndpointClass;
940 pEndpoint->pTasksFreeHead = NULL;
941 pEndpoint->pTasksFreeTail = NULL;
942 pEndpoint->cTasksCached = 0;
943 pEndpoint->uTaskIdNext = 0;
944 pEndpoint->fTaskIdWraparound = false;
945 pEndpoint->pTemplate = pTemplate;
946 pEndpoint->iSlotStart = pEndpointClass->cEndpoints % RT_ELEMENTS(pEndpointClass->apTaskCache);
947 pEndpoint->pszUri = RTStrDup(pszFilename);
948 pEndpoint->cUsers = 1;
949 if (pEndpoint->pszUri)
950 {
951 /* Init the cache. */
952 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
953 pEndpointClass->pEndpointOps->cbTask,
954 (void **)&pEndpoint->pTasksFreeHead);
955 if (RT_SUCCESS(rc))
956 {
957 pEndpoint->pTasksFreeTail = pEndpoint->pTasksFreeHead;
958
959 /* Call the initializer for the endpoint. */
960 rc = pEndpointClass->pEndpointOps->pfnEpInitialize(pEndpoint, pszFilename, fFlags);
961 if (RT_SUCCESS(rc))
962 {
963 /* Link it into the list of endpoints. */
964 rc = RTCritSectEnter(&pEndpointClass->CritSect);
965 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
966
967 pEndpoint->pNext = pEndpointClass->pEndpointsHead;
968 if (pEndpointClass->pEndpointsHead)
969 pEndpointClass->pEndpointsHead->pPrev = pEndpoint;
970
971 pEndpointClass->pEndpointsHead = pEndpoint;
972 pEndpointClass->cEndpoints++;
973
974 rc = RTCritSectLeave(&pEndpointClass->CritSect);
975 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
976
977 /* Reference the template. */
978 ASMAtomicIncU32(&pTemplate->cUsed);
979
980 *ppEndpoint = pEndpoint;
981
982 LogFlowFunc((": Created endpoint for %s: rc=%Rrc\n", pszFilename, rc));
983 return VINF_SUCCESS;
984 }
985 MMR3HeapFree(pEndpoint->pTasksFreeHead);
986 RTStrFree(pEndpoint->pszUri);
987 }
988 else
989 rc = VERR_NO_MEMORY;
990 }
991 MMR3HeapFree(pEndpoint);
992 }
993 }
994 else
995 {
996 /* Endpoint found. */
997 pEndpoint->cUsers++;
998
999 *ppEndpoint = pEndpoint;
1000 return VINF_SUCCESS;
1001 }
1002
1003 LogFlowFunc((": Creation of endpoint for %s failed: rc=%Rrc\n", pszFilename, rc));
1004
1005 return rc;
1006}
1007
1008VMMR3DECL(void) PDMR3AsyncCompletionEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1009{
1010 LogFlowFunc((": pEndpoint=%p\n", pEndpoint));
1011
1012 /* Sanity checks. */
1013 AssertReturnVoid(VALID_PTR(pEndpoint));
1014
1015 pEndpoint->cUsers--;
1016
1017 /* If the last user closed the endpoint we will free it. */
1018 if (!pEndpoint->cUsers)
1019 {
1020 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
1021 pEndpointClass->pEndpointOps->pfnEpClose(pEndpoint);
1022
1023 /* Free cached tasks. */
1024 PPDMASYNCCOMPLETIONTASK pTask = pEndpoint->pTasksFreeHead;
1025
1026 while (pTask)
1027 {
1028 PPDMASYNCCOMPLETIONTASK pTaskFree = pTask;
1029 pTask = pTask->pNext;
1030 MMR3HeapFree(pTaskFree);
1031 }
1032
1033 /* Drop reference from the template. */
1034 ASMAtomicDecU32(&pEndpoint->pTemplate->cUsed);
1035
1036 /* Unlink the endpoint from the list. */
1037 int rc = RTCritSectEnter(&pEndpointClass->CritSect);
1038 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
1039
1040 PPDMASYNCCOMPLETIONENDPOINT pEndpointNext = pEndpoint->pNext;
1041 PPDMASYNCCOMPLETIONENDPOINT pEndpointPrev = pEndpoint->pPrev;
1042
1043 if (pEndpointPrev)
1044 pEndpointPrev->pNext = pEndpointNext;
1045 else
1046 pEndpointClass->pEndpointsHead = pEndpointNext;
1047 if (pEndpointNext)
1048 pEndpointNext->pPrev = pEndpointPrev;
1049
1050 pEndpointClass->cEndpoints--;
1051
1052 rc = RTCritSectLeave(&pEndpointClass->CritSect);
1053 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
1054
1055 RTStrFree(pEndpoint->pszUri);
1056 MMR3HeapFree(pEndpoint);
1057 }
1058}
1059
1060VMMR3DECL(int) PDMR3AsyncCompletionEpRead(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1061 PCPDMDATASEG paSegments, size_t cSegments,
1062 size_t cbRead, void *pvUser,
1063 PPPDMASYNCCOMPLETIONTASK ppTask)
1064{
1065 int rc = VINF_SUCCESS;
1066
1067 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1068 AssertReturn(VALID_PTR(paSegments), VERR_INVALID_POINTER);
1069 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1070 AssertReturn(cSegments > 0, VERR_INVALID_PARAMETER);
1071 AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
1072 AssertReturn(off >= 0, VERR_INVALID_PARAMETER);
1073
1074 PPDMASYNCCOMPLETIONTASK pTask;
1075
1076 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1077 if (!pTask)
1078 return VERR_NO_MEMORY;
1079
1080 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpRead(pTask, pEndpoint, off,
1081 paSegments, cSegments, cbRead);
1082 if (RT_SUCCESS(rc))
1083 {
1084 *ppTask = pTask;
1085 }
1086 else
1087 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1088
1089 return rc;
1090}
1091
1092VMMR3DECL(int) PDMR3AsyncCompletionEpWrite(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1093 PCPDMDATASEG paSegments, size_t cSegments,
1094 size_t cbWrite, void *pvUser,
1095 PPPDMASYNCCOMPLETIONTASK ppTask)
1096{
1097 int rc = VINF_SUCCESS;
1098
1099 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1100 AssertReturn(VALID_PTR(paSegments), VERR_INVALID_POINTER);
1101 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1102 AssertReturn(cSegments > 0, VERR_INVALID_PARAMETER);
1103 AssertReturn(cbWrite > 0, VERR_INVALID_PARAMETER);
1104 AssertReturn(off >= 0, VERR_INVALID_PARAMETER);
1105
1106 PPDMASYNCCOMPLETIONTASK pTask;
1107
1108 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1109 if (!pTask)
1110 return VERR_NO_MEMORY;
1111
1112 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpWrite(pTask, pEndpoint, off,
1113 paSegments, cSegments, cbWrite);
1114 if (RT_SUCCESS(rc))
1115 {
1116 *ppTask = pTask;
1117 }
1118 else
1119 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1120
1121 return rc;
1122}
1123
1124VMMR3DECL(int) PDMR3AsyncCompletionEpFlush(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
1125 void *pvUser,
1126 PPPDMASYNCCOMPLETIONTASK ppTask)
1127{
1128 int rc = VINF_SUCCESS;
1129
1130 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1131 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1132
1133 PPDMASYNCCOMPLETIONTASK pTask;
1134
1135 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1136 if (!pTask)
1137 return VERR_NO_MEMORY;
1138
1139 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpFlush(pTask, pEndpoint);
1140 if (RT_SUCCESS(rc))
1141 {
1142 *ppTask = pTask;
1143 }
1144 else
1145 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1146
1147 return rc;
1148}
1149
1150VMMR3DECL(int) PDMR3AsyncCompletionEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
1151 uint64_t *pcbSize)
1152{
1153 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1154 AssertReturn(VALID_PTR(pcbSize), VERR_INVALID_POINTER);
1155
1156 return pEndpoint->pEpClass->pEndpointOps->pfnEpGetSize(pEndpoint, pcbSize);
1157}
1158
1159VMMR3DECL(int) PDMR3AsyncCompletionTaskCancel(PPDMASYNCCOMPLETIONTASK pTask)
1160{
1161 return VERR_NOT_IMPLEMENTED;
1162}
1163
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