VirtualBox

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

Last change on this file since 26944 was 26147, checked in by vboxsync, 15 years ago

AsyncCompletion: Fix incorrect count of outstanding write tasks. Fixes hangs during flush requests. Return VINF_AIO_TASK_PENDING if data needs to be read from the file

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.0 KB
Line 
1/* $Id: PDMAsyncCompletion.cpp 26147 2010-02-02 13:55:20Z 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#include <iprt/path.h>
42
43#include <VBox/pdmasynccompletion.h>
44#include "PDMAsyncCompletionInternal.h"
45
46/**
47 * Async I/O type.
48 */
49typedef enum PDMASYNCCOMPLETIONTEMPLATETYPE
50{
51 /** Device . */
52 PDMASYNCCOMPLETIONTEMPLATETYPE_DEV = 1,
53 /** Driver consumer. */
54 PDMASYNCCOMPLETIONTEMPLATETYPE_DRV,
55 /** Internal consumer. */
56 PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL,
57 /** Usb consumer. */
58 PDMASYNCCOMPLETIONTEMPLATETYPE_USB
59} PDMASYNCTEMPLATETYPE;
60
61/**
62 * PDM Async I/O template.
63 */
64typedef struct PDMASYNCCOMPLETIONTEMPLATE
65{
66 /** Pointer to the next template in the list. */
67 R3PTRTYPE(PPDMASYNCCOMPLETIONTEMPLATE) pNext;
68 /** Pointer to the previous template in the list. */
69 R3PTRTYPE(PPDMASYNCCOMPLETIONTEMPLATE) pPrev;
70 /** Type specific data. */
71 union
72 {
73 /** PDMASYNCCOMPLETIONTEMPLATETYPE_DEV */
74 struct
75 {
76 /** Pointer to consumer function. */
77 R3PTRTYPE(PFNPDMASYNCCOMPLETEDEV) pfnCompleted;
78 /** Pointer to the device instance owning the template. */
79 R3PTRTYPE(PPDMDEVINS) pDevIns;
80 } Dev;
81 /** PDMASYNCCOMPLETIONTEMPLATETYPE_DRV */
82 struct
83 {
84 /** Pointer to consumer function. */
85 R3PTRTYPE(PFNPDMASYNCCOMPLETEDRV) pfnCompleted;
86 /** Pointer to the driver instance owning the template. */
87 R3PTRTYPE(PPDMDRVINS) pDrvIns;
88 /** User agument given during template creation.
89 * This is only here to make things much easier
90 * for DrVVD. */
91 void *pvTemplateUser;
92 } Drv;
93 /** PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL */
94 struct
95 {
96 /** Pointer to consumer function. */
97 R3PTRTYPE(PFNPDMASYNCCOMPLETEINT) pfnCompleted;
98 /** Pointer to user data. */
99 R3PTRTYPE(void *) pvUser;
100 } Int;
101 /** PDMASYNCCOMPLETIONTEMPLATETYPE_USB */
102 struct
103 {
104 /** Pointer to consumer function. */
105 R3PTRTYPE(PFNPDMASYNCCOMPLETEUSB) pfnCompleted;
106 /** Pointer to the usb instance owning the template. */
107 R3PTRTYPE(PPDMUSBINS) pUsbIns;
108 } Usb;
109 } u;
110 /** Template type. */
111 PDMASYNCCOMPLETIONTEMPLATETYPE enmType;
112 /** Pointer to the VM. */
113 R3PTRTYPE(PVM) pVM;
114 /** Use count of the template. */
115 volatile uint32_t cUsed;
116} PDMASYNCCOMPLETIONTEMPLATE;
117
118static void pdmR3AsyncCompletionPutTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, PPDMASYNCCOMPLETIONTASK pTask, bool fLocal);
119
120/**
121 * Internal worker for the creation apis
122 *
123 * @returns VBox status.
124 * @param pVM VM handle.
125 * @param ppTemplate Where to store the template handle.
126 */
127static int pdmR3AsyncCompletionTemplateCreate(PVM pVM, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE enmType)
128{
129 int rc = VINF_SUCCESS;
130
131 if (ppTemplate == NULL)
132 {
133 AssertMsgFailed(("ppTemplate is NULL\n"));
134 return VERR_INVALID_PARAMETER;
135 }
136
137 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
138 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMASYNCCOMPLETIONTEMPLATE), (void **)&pTemplate);
139 if (RT_FAILURE(rc))
140 return rc;
141
142 /*
143 * Initialize fields.
144 */
145 pTemplate->pVM = pVM;
146 pTemplate->cUsed = 0;
147 pTemplate->enmType = enmType;
148
149 /*
150 * Add template to the global VM template list.
151 */
152 pTemplate->pNext = pVM->pdm.s.pAsyncCompletionTemplates;
153 if (pVM->pdm.s.pAsyncCompletionTemplates)
154 pVM->pdm.s.pAsyncCompletionTemplates->pPrev = pTemplate;
155 pVM->pdm.s.pAsyncCompletionTemplates = pTemplate;
156
157 *ppTemplate = pTemplate;
158 return VINF_SUCCESS;
159}
160
161/**
162 * Creates a async completion template for a device instance.
163 *
164 * The template is used when creating new completion tasks.
165 *
166 * @returns VBox status code.
167 * @param pVM Pointer to the shared VM structure.
168 * @param pDevIns The device instance.
169 * @param ppTemplate Where to store the template pointer on success.
170 * @param pfnCompleted The completion callback routine.
171 * @param pszDesc Description.
172 */
173VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateDevice(PVM pVM, PPDMDEVINS pDevIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEDEV pfnCompleted, const char *pszDesc)
174{
175 LogFlow(("%s: pDevIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n",
176 __FUNCTION__, pDevIns, ppTemplate, pfnCompleted, pszDesc));
177
178 /*
179 * Validate input.
180 */
181 VM_ASSERT_EMT(pVM);
182 if (!pfnCompleted)
183 {
184 AssertMsgFailed(("No completion callback!\n"));
185 return VERR_INVALID_PARAMETER;
186 }
187
188 if (!ppTemplate)
189 {
190 AssertMsgFailed(("Template pointer is NULL!\n"));
191 return VERR_INVALID_PARAMETER;
192 }
193
194 /*
195 * Create the template.
196 */
197 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
198 int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_DEV);
199 if (RT_SUCCESS(rc))
200 {
201 pTemplate->u.Dev.pDevIns = pDevIns;
202 pTemplate->u.Dev.pfnCompleted = pfnCompleted;
203
204 *ppTemplate = pTemplate;
205 Log(("PDM: Created device template %p: pfnCompleted=%p pDevIns=%p\n",
206 pTemplate, pfnCompleted, pDevIns));
207 }
208
209 return rc;
210}
211
212/**
213 * Creates a async completion template for a driver instance.
214 *
215 * The template is used when creating new completion tasks.
216 *
217 * @returns VBox status code.
218 * @param pVM Pointer to the shared VM structure.
219 * @param pDrvIns The driver instance.
220 * @param ppTemplate Where to store the template pointer on success.
221 * @param pfnCompleted The completion callback routine.
222 * @param pvTemplateUser Template user argument
223 * @param pszDesc Description.
224 */
225VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEDRV pfnCompleted, void *pvTemplateUser, const char *pszDesc)
226{
227 LogFlow(("%s: pDrvIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n",
228 __FUNCTION__, pDrvIns, ppTemplate, pfnCompleted, pszDesc));
229
230 /*
231 * Validate input.
232 */
233 VM_ASSERT_EMT(pVM);
234 if (!pfnCompleted)
235 {
236 AssertMsgFailed(("No completion callback!\n"));
237 return VERR_INVALID_PARAMETER;
238 }
239
240 if (!ppTemplate)
241 {
242 AssertMsgFailed(("Template pointer is NULL!\n"));
243 return VERR_INVALID_PARAMETER;
244 }
245
246 /*
247 * Create the template.
248 */
249 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
250 int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_DRV);
251 if (RT_SUCCESS(rc))
252 {
253 pTemplate->u.Drv.pDrvIns = pDrvIns;
254 pTemplate->u.Drv.pfnCompleted = pfnCompleted;
255 pTemplate->u.Drv.pvTemplateUser = pvTemplateUser;
256
257 *ppTemplate = pTemplate;
258 Log(("PDM: Created driver template %p: pfnCompleted=%p pDrvIns=%p\n",
259 pTemplate, pfnCompleted, pDrvIns));
260 }
261
262 return rc;
263}
264
265/**
266 * Creates a async completion template for a USB device instance.
267 *
268 * The template is used when creating new completion tasks.
269 *
270 * @returns VBox status code.
271 * @param pVM Pointer to the shared VM structure.
272 * @param pUsbIns The USB device instance.
273 * @param ppTemplate Where to store the template pointer on success.
274 * @param pfnCompleted The completion callback routine.
275 * @param pszDesc Description.
276 */
277VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateUsb(PVM pVM, PPDMUSBINS pUsbIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEUSB pfnCompleted, const char *pszDesc)
278{
279 LogFlow(("%s: pUsbIns=%p ppTemplate=%p pfnCompleted=%p pszDesc=%s\n",
280 __FUNCTION__, pUsbIns, ppTemplate, pfnCompleted, pszDesc));
281
282 /*
283 * Validate input.
284 */
285 VM_ASSERT_EMT(pVM);
286 if (!pfnCompleted)
287 {
288 AssertMsgFailed(("No completion callback!\n"));
289 return VERR_INVALID_PARAMETER;
290 }
291
292 if (!ppTemplate)
293 {
294 AssertMsgFailed(("Template pointer is NULL!\n"));
295 return VERR_INVALID_PARAMETER;
296 }
297
298 /*
299 * Create the template.
300 */
301 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
302 int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_USB);
303 if (RT_SUCCESS(rc))
304 {
305 pTemplate->u.Usb.pUsbIns = pUsbIns;
306 pTemplate->u.Usb.pfnCompleted = pfnCompleted;
307
308 *ppTemplate = pTemplate;
309 Log(("PDM: Created usb template %p: pfnCompleted=%p pDevIns=%p\n",
310 pTemplate, pfnCompleted, pUsbIns));
311 }
312
313 return rc;
314}
315
316/**
317 * Creates a async completion template for internally by the VMM.
318 *
319 * The template is used when creating new completion tasks.
320 *
321 * @returns VBox status code.
322 * @param pVM Pointer to the shared VM structure.
323 * @param ppTemplate Where to store the template pointer on success.
324 * @param pfnCompleted The completion callback routine.
325 * @param pvUser2 The 2nd user argument for the callback.
326 * @param pszDesc Description.
327 */
328VMMR3DECL(int) PDMR3AsyncCompletionTemplateCreateInternal(PVM pVM, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate, PFNPDMASYNCCOMPLETEINT pfnCompleted, void *pvUser2, const char *pszDesc)
329{
330 LogFlow(("%s: ppTemplate=%p pfnCompleted=%p pvUser2=%p pszDesc=%s\n",
331 __FUNCTION__, ppTemplate, pfnCompleted, pvUser2, pszDesc));
332
333 /*
334 * Validate input.
335 */
336 VM_ASSERT_EMT(pVM);
337 if (!pfnCompleted)
338 {
339 AssertMsgFailed(("No completion callback!\n"));
340 return VERR_INVALID_PARAMETER;
341 }
342
343 if (!ppTemplate)
344 {
345 AssertMsgFailed(("Template pointer is NULL!\n"));
346 return VERR_INVALID_PARAMETER;
347 }
348
349 /*
350 * Create the template.
351 */
352 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
353 int rc = pdmR3AsyncCompletionTemplateCreate(pVM, &pTemplate, PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL);
354 if (RT_SUCCESS(rc))
355 {
356 pTemplate->u.Int.pvUser = pvUser2;
357 pTemplate->u.Int.pfnCompleted = pfnCompleted;
358
359 *ppTemplate = pTemplate;
360 Log(("PDM: Created internal template %p: pfnCompleted=%p pvUser2=%p\n",
361 pTemplate, pfnCompleted, pvUser2));
362 }
363
364 return rc;
365}
366
367/**
368 * Destroys the specified async completion template.
369 *
370 * @returns VBox status codes:
371 * @retval VINF_SUCCESS on success.
372 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if the template is still in use.
373 *
374 * @param pTemplate The template in question.
375 */
376VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroy(PPDMASYNCCOMPLETIONTEMPLATE pTemplate)
377{
378 LogFlow(("%s: pTemplate=%p\n", __FUNCTION__, pTemplate));
379
380 if (!pTemplate)
381 {
382 AssertMsgFailed(("pTemplate is NULL!\n"));
383 return VERR_INVALID_PARAMETER;
384 }
385
386 /*
387 * Check if the template is still used.
388 */
389 if (pTemplate->cUsed > 0)
390 {
391 AssertMsgFailed(("Template is still in use\n"));
392 return VERR_PDM_ASYNC_TEMPLATE_BUSY;
393 }
394
395 /*
396 * Unlink the template from the list.
397 */
398 PVM pVM = pTemplate->pVM;
399 PPDMASYNCCOMPLETIONTEMPLATE pPrev = pTemplate->pPrev;
400 PPDMASYNCCOMPLETIONTEMPLATE pNext = pTemplate->pNext;
401
402 if (pPrev)
403 pPrev->pNext = pNext;
404 else
405 pVM->pdm.s.pAsyncCompletionTemplates = pNext;
406
407 if (pNext)
408 pNext->pPrev = pPrev;
409
410 /*
411 * Free the template.
412 */
413 MMR3HeapFree(pTemplate);
414
415 return VINF_SUCCESS;
416}
417
418/**
419 * Destroys all the specified async completion templates for the given device instance.
420 *
421 * @returns VBox status codes:
422 * @retval VINF_SUCCESS on success.
423 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
424 *
425 * @param pVM Pointer to the shared VM structure.
426 * @param pDevIns The device instance.
427 */
428VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
429{
430 LogFlow(("%s: pDevIns=%p\n", __FUNCTION__, pDevIns));
431
432 /*
433 * Validate input.
434 */
435 if (!pDevIns)
436 return VERR_INVALID_PARAMETER;
437 VM_ASSERT_EMT(pVM);
438
439 /*
440 * Unlink it.
441 */
442 PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pVM->pdm.s.pAsyncCompletionTemplates;
443 while (pTemplate)
444 {
445 if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_DEV
446 && pTemplate->u.Dev.pDevIns == pDevIns)
447 {
448 PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate;
449 pTemplate = pTemplate->pNext;
450 int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy);
451 if (RT_FAILURE(rc))
452 return rc;
453 }
454 else
455 pTemplate = pTemplate->pNext;
456 }
457
458 return VINF_SUCCESS;
459}
460
461/**
462 * Destroys all the specified async completion templates for the given driver instance.
463 *
464 * @returns VBox status codes:
465 * @retval VINF_SUCCESS on success.
466 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
467 *
468 * @param pVM Pointer to the shared VM structure.
469 * @param pDrvIns The driver instance.
470 */
471VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
472{
473 LogFlow(("%s: pDevIns=%p\n", __FUNCTION__, pDrvIns));
474
475 /*
476 * Validate input.
477 */
478 if (!pDrvIns)
479 return VERR_INVALID_PARAMETER;
480 VM_ASSERT_EMT(pVM);
481
482 /*
483 * Unlink it.
484 */
485 PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pVM->pdm.s.pAsyncCompletionTemplates;
486 while (pTemplate)
487 {
488 if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_DRV
489 && pTemplate->u.Drv.pDrvIns == pDrvIns)
490 {
491 PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate;
492 pTemplate = pTemplate->pNext;
493 int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy);
494 if (RT_FAILURE(rc))
495 return rc;
496 }
497 else
498 pTemplate = pTemplate->pNext;
499 }
500
501 return VINF_SUCCESS;
502}
503
504/**
505 * Destroys all the specified async completion templates for the given USB device instance.
506 *
507 * @returns VBox status codes:
508 * @retval VINF_SUCCESS on success.
509 * @retval VERR_PDM_ASYNC_TEMPLATE_BUSY if one or more of the templates are still in use.
510 *
511 * @param pVM Pointer to the shared VM structure.
512 * @param pUsbIns The USB device instance.
513 */
514VMMR3DECL(int) PDMR3AsyncCompletionTemplateDestroyUsb(PVM pVM, PPDMUSBINS pUsbIns)
515{
516 LogFlow(("%s: pUsbIns=%p\n", __FUNCTION__, pUsbIns));
517
518 /*
519 * Validate input.
520 */
521 if (!pUsbIns)
522 return VERR_INVALID_PARAMETER;
523 VM_ASSERT_EMT(pVM);
524
525 /*
526 * Unlink it.
527 */
528 PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pVM->pdm.s.pAsyncCompletionTemplates;
529 while (pTemplate)
530 {
531 if ( pTemplate->enmType == PDMASYNCCOMPLETIONTEMPLATETYPE_USB
532 && pTemplate->u.Usb.pUsbIns == pUsbIns)
533 {
534 PPDMASYNCCOMPLETIONTEMPLATE pTemplateDestroy = pTemplate;
535 pTemplate = pTemplate->pNext;
536 int rc = PDMR3AsyncCompletionTemplateDestroy(pTemplateDestroy);
537 if (RT_FAILURE(rc))
538 return rc;
539 }
540 else
541 pTemplate = pTemplate->pNext;
542 }
543
544 return VINF_SUCCESS;
545}
546
547void pdmR3AsyncCompletionCompleteTask(PPDMASYNCCOMPLETIONTASK pTask, bool fCallCompletionHandler)
548{
549 LogFlow(("%s: pTask=%#p fCallCompletionHandler=%RTbool\n", __FUNCTION__, pTask, fCallCompletionHandler));
550
551 if (fCallCompletionHandler)
552 {
553 PPDMASYNCCOMPLETIONTEMPLATE pTemplate = pTask->pEndpoint->pTemplate;
554
555 switch (pTemplate->enmType)
556 {
557 case PDMASYNCCOMPLETIONTEMPLATETYPE_DEV:
558 {
559 pTemplate->u.Dev.pfnCompleted(pTemplate->u.Dev.pDevIns, pTask->pvUser);
560 break;
561 }
562 case PDMASYNCCOMPLETIONTEMPLATETYPE_DRV:
563 {
564 pTemplate->u.Drv.pfnCompleted(pTemplate->u.Drv.pDrvIns, pTemplate->u.Drv.pvTemplateUser, pTask->pvUser);
565 break;
566 }
567 case PDMASYNCCOMPLETIONTEMPLATETYPE_USB:
568 {
569 pTemplate->u.Usb.pfnCompleted(pTemplate->u.Usb.pUsbIns, pTask->pvUser);
570 break;
571 }
572 case PDMASYNCCOMPLETIONTEMPLATETYPE_INTERNAL:
573 {
574 pTemplate->u.Int.pfnCompleted(pTemplate->pVM, pTask->pvUser, pTemplate->u.Int.pvUser);
575 break;
576 }
577 default:
578 AssertMsgFailed(("Unknown template type!\n"));
579 }
580 }
581
582 pdmR3AsyncCompletionPutTask(pTask->pEndpoint, pTask, true);
583}
584
585/**
586 * Worker initializing a endpoint class.
587 *
588 * @returns VBox statis code.
589 * @param pVM Pointer to the shared VM instance data.
590 * @param pEpClass Pointer to the endpoint class structure.
591 * @param pCfgHandle Pointer to the the CFGM tree.
592 */
593int pdmR3AsyncCompletionEpClassInit(PVM pVM, PCPDMASYNCCOMPLETIONEPCLASSOPS pEpClassOps, PCFGMNODE pCfgHandle)
594{
595 int rc = VINF_SUCCESS;
596
597 /* Validate input. */
598 if ( !pEpClassOps
599 || (pEpClassOps->u32Version != PDMAC_EPCLASS_OPS_VERSION)
600 || (pEpClassOps->u32VersionEnd != PDMAC_EPCLASS_OPS_VERSION))
601 AssertMsgFailedReturn(("Invalid endpoint class data\n"), VERR_VERSION_MISMATCH);
602
603 LogFlowFunc((": pVM=%p pEpClassOps=%p{%s}\n", pVM, pEpClassOps, pEpClassOps->pcszName));
604
605 /* Allocate global class data. */
606 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = NULL;
607
608 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
609 pEpClassOps->cbEndpointClassGlobal,
610 (void **)&pEndpointClass);
611 if (RT_SUCCESS(rc))
612 {
613 /* Initialize common data. */
614 pEndpointClass->pVM = pVM;
615 pEndpointClass->pEndpointOps = pEpClassOps;
616
617 rc = RTCritSectInit(&pEndpointClass->CritSect);
618 if (RT_SUCCESS(rc))
619 {
620 PCFGMNODE pCfgNodeClass = CFGMR3GetChild(pCfgHandle, pEpClassOps->pcszName);
621
622 /* Query the common CFGM options */
623 rc = CFGMR3QueryU32Def(pCfgNodeClass, "TaskCachePerEndpoint", &pEndpointClass->cEndpointCacheSize, 5);
624 AssertRCReturn(rc, rc);
625
626 rc = CFGMR3QueryU32Def(pCfgNodeClass, "TaskCachePerClass", &pEndpointClass->cEpClassCacheSize, 50);
627 AssertRCReturn(rc, rc);
628
629 /* Call the specific endpoint class initializer. */
630 rc = pEpClassOps->pfnInitialize(pEndpointClass, pCfgNodeClass);
631 if (RT_SUCCESS(rc))
632 {
633 AssertMsg(!pVM->pdm.s.papAsyncCompletionEndpointClass[pEpClassOps->enmClassType],
634 ("Endpoint class was already initialized\n"));
635
636 pVM->pdm.s.papAsyncCompletionEndpointClass[pEpClassOps->enmClassType] = pEndpointClass;
637 LogFlowFunc((": Initialized endpoint class \"%s\" rc=%Rrc\n", pEpClassOps->pcszName, rc));
638 return VINF_SUCCESS;
639 }
640 RTCritSectDelete(&pEndpointClass->CritSect);
641 }
642 MMR3HeapFree(pEndpointClass);
643 }
644
645 LogFlowFunc((": Failed to initialize endpoint class rc=%Rrc\n", rc));
646
647 return rc;
648}
649
650/**
651 * Worker terminating all endpoint classes.
652 *
653 * @returns nothing
654 * @param pEndpointClass Pointer to the endpoint class to terminate.
655 *
656 * @remarks This method ensures that any still open endpoint is closed.
657 */
658static void pdmR3AsyncCompletionEpClassTerminate(PPDMASYNCCOMPLETIONEPCLASS pEndpointClass)
659{
660 int rc = VINF_SUCCESS;
661 PVM pVM = pEndpointClass->pVM;
662
663 /* Close all still open endpoints. */
664 while (pEndpointClass->pEndpointsHead)
665 PDMR3AsyncCompletionEpClose(pEndpointClass->pEndpointsHead);
666
667 /* Destroy all cached tasks. */
668 for (unsigned i = 0; i < RT_ELEMENTS(pEndpointClass->apTaskCache); i++)
669 {
670 PPDMASYNCCOMPLETIONTASK pTask = pEndpointClass->apTaskCache[i];
671
672 while (pTask)
673 {
674 PPDMASYNCCOMPLETIONTASK pTaskFree = pTask;
675 pTask = pTask->pNext;
676 MMR3HeapFree(pTaskFree);
677 }
678 }
679
680 /* Call the termination callback of the class. */
681 pEndpointClass->pEndpointOps->pfnTerminate(pEndpointClass);
682
683 RTCritSectDelete(&pEndpointClass->CritSect);
684
685 /* Free the memory of the class finally and clear the entry in the class array. */
686 pVM->pdm.s.papAsyncCompletionEndpointClass[pEndpointClass->pEndpointOps->enmClassType] = NULL;
687 MMR3HeapFree(pEndpointClass);
688}
689
690/**
691 * Initialize the async completion manager.
692 *
693 * @returns VBox status code
694 * @param pVM Pointer to the shared VM structure.
695 */
696int pdmR3AsyncCompletionInit(PVM pVM)
697{
698 int rc = VINF_SUCCESS;
699
700 LogFlowFunc((": pVM=%p\n", pVM));
701
702 VM_ASSERT_EMT(pVM);
703
704 do
705 {
706 /* Allocate array for global class data. */
707 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
708 sizeof(PPDMASYNCCOMPLETIONEPCLASS) * PDMASYNCCOMPLETIONEPCLASSTYPE_MAX,
709 (void **)&pVM->pdm.s.papAsyncCompletionEndpointClass);
710 if (RT_FAILURE(rc))
711 break;
712
713 PCFGMNODE pCfgRoot = CFGMR3GetRoot(pVM);
714 PCFGMNODE pCfgAsyncCompletion = CFGMR3GetChild(CFGMR3GetChild(pCfgRoot, "PDM"), "AsyncCompletion");
715
716 rc = pdmR3AsyncCompletionEpClassInit(pVM, &g_PDMAsyncCompletionEndpointClassFile, pCfgAsyncCompletion);
717 if (RT_FAILURE(rc))
718 break;
719
720 /* Put other classes here. */
721 } while (0);
722
723 LogFlowFunc((": pVM=%p rc=%Rrc\n", pVM, rc));
724
725 return rc;
726}
727
728/**
729 * Terminates the async completion manager.
730 *
731 * @returns VBox status code
732 * @param pVM Pointer to the shared VM structure.
733 */
734int pdmR3AsyncCompletionTerm(PVM pVM)
735{
736 LogFlowFunc((": pVM=%p\n", pVM));
737
738 if (pVM->pdm.s.papAsyncCompletionEndpointClass)
739 {
740 pdmR3AsyncCompletionEpClassTerminate(pVM->pdm.s.papAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE]);
741 MMR3HeapFree(pVM->pdm.s.papAsyncCompletionEndpointClass);
742 }
743 return VINF_SUCCESS;
744}
745
746/**
747 * Tries to get a free task from the endpoint or class cache
748 * allocating the task if it fails.
749 *
750 * @returns Pointer to a new and initialized task or NULL
751 * @param pEndpoint The endpoint the task is for.
752 * @param pvUser Opaque user data for the task.
753 */
754static PPDMASYNCCOMPLETIONTASK pdmR3AsyncCompletionGetTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, void *pvUser)
755{
756 PPDMASYNCCOMPLETIONTASK pTask = NULL;
757
758 /* Try the small per endpoint cache first. */
759 uint32_t cTasksCached = ASMAtomicReadU32(&pEndpoint->cTasksCached);
760 if (cTasksCached == 0)
761 {
762 /* Try the bigger per endpoint class cache. */
763 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
764
765 /* We start with the assigned slot id to distribute the load when allocating new tasks. */
766 unsigned iSlot = pEndpoint->iSlotStart;
767 do
768 {
769 pTask = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
770 if (pTask)
771 break;
772
773 iSlot = (iSlot + 1) % RT_ELEMENTS(pEndpointClass->apTaskCache);
774 } while (iSlot != pEndpoint->iSlotStart);
775
776 if (!pTask)
777 {
778 /*
779 * Allocate completely new.
780 * If this fails we return NULL.
781 */
782 int rc = MMR3HeapAllocZEx(pEndpointClass->pVM, MM_TAG_PDM_ASYNC_COMPLETION,
783 pEndpointClass->pEndpointOps->cbTask,
784 (void **)&pTask);
785 if (RT_FAILURE(rc))
786 pTask = NULL;
787 }
788 else
789 {
790 /* Remove the first element and put the rest into the slot again. */
791 PPDMASYNCCOMPLETIONTASK pTaskHeadNew = pTask->pNext;
792
793 /* Put back into the list adding any new tasks. */
794 while (true)
795 {
796 bool fChanged = ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], pTaskHeadNew, NULL);
797
798 if (fChanged)
799 break;
800
801 PPDMASYNCCOMPLETIONTASK pTaskHead = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
802
803 /* The new task could be taken inbetween */
804 if (pTaskHead)
805 {
806 /* Go to the end of the probably much shorter new list. */
807 PPDMASYNCCOMPLETIONTASK pTaskTail = pTaskHead;
808 while (pTaskTail->pNext)
809 pTaskTail = pTaskTail->pNext;
810
811 /* Concatenate */
812 pTaskTail->pNext = pTaskHeadNew;
813
814 pTaskHeadNew = pTaskHead;
815 }
816 /* Another round trying to change the list. */
817 }
818 /* We got a task from the global cache so decrement the counter */
819 ASMAtomicDecU32(&pEndpointClass->cTasksCached);
820 }
821 }
822 else
823 {
824 /* Grab a free task from the head. */
825 AssertMsg(pEndpoint->cTasksCached > 0, ("No tasks cached but list contain more than one element\n"));
826
827 pTask = pEndpoint->pTasksFreeHead;
828 pEndpoint->pTasksFreeHead = pTask->pNext;
829 ASMAtomicDecU32(&pEndpoint->cTasksCached);
830 }
831
832 if (RT_LIKELY(pTask))
833 {
834 /* Get ID of the task. */
835 pTask->uTaskId = ASMAtomicIncU32(&pEndpoint->uTaskIdNext);
836
837 /* Initialize common parts. */
838 pTask->pvUser = pvUser;
839 pTask->pEndpoint = pEndpoint;
840 /* Clear list pointers for safety. */
841 pTask->pPrev = NULL;
842 pTask->pNext = NULL;
843#ifdef VBOX_WITH_STATISTICS
844 pTask->tsNsStart = RTTimeNanoTS();
845 STAM_COUNTER_INC(&pEndpoint->StatIoOpsStarted);
846#endif
847 }
848
849 return pTask;
850}
851
852/**
853 * Puts a task in one of the caches.
854 *
855 * @returns nothing.
856 * @param pEndpoint The endpoint the task belongs to.
857 * @param pTask The task to cache.
858 * @param fLocal Whether the per endpoint cache should be tried first.
859 */
860static void pdmR3AsyncCompletionPutTask(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, PPDMASYNCCOMPLETIONTASK pTask, bool fLocal)
861{
862 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
863
864#ifdef VBOX_WITH_STATISTICS
865 uint64_t tsRun = RTTimeNanoTS() - pTask->tsNsStart;
866 uint64_t iStatIdx;
867
868 if (tsRun < 1000)
869 {
870 /* Update nanoseconds statistics */
871 iStatIdx = tsRun / 100;
872 STAM_COUNTER_INC(&pEndpoint->StatTaskRunTimesNs[iStatIdx]);
873 }
874 else
875 {
876 tsRun /= 1000;
877
878 if (tsRun < 1000)
879 {
880 /* Update microsecnds statistics */
881 iStatIdx = tsRun / 100;
882 STAM_COUNTER_INC(&pEndpoint->StatTaskRunTimesMicroSec[iStatIdx]);
883 }
884 else
885 {
886 tsRun /= 1000;
887
888 if (tsRun < 1000)
889 {
890 /* Update milliseconds statistics */
891 iStatIdx = tsRun / 100;
892 STAM_COUNTER_INC(&pEndpoint->StatTaskRunTimesMs[iStatIdx]);
893 }
894 else
895 {
896 tsRun /= 1000;
897
898 if (tsRun < 1000)
899 {
900 /* Update seconds statistics */
901 iStatIdx = tsRun / 10;
902 STAM_COUNTER_INC(&pEndpoint->StatTaskRunTimesSec[iStatIdx]);
903 }
904 else
905 STAM_COUNTER_INC(&pEndpoint->StatTaskRunOver100Sec);
906 }
907 }
908 }
909
910 STAM_COUNTER_INC(&pEndpoint->StatIoOpsCompleted);
911 pEndpoint->cIoOpsCompleted++;
912 uint64_t tsMsCur = RTTimeMilliTS();
913 uint64_t tsInterval = tsMsCur - pEndpoint->tsIntervalStartMs;
914
915 if (tsInterval >= 1000)
916 {
917 pEndpoint->StatIoOpsPerSec.c = pEndpoint->cIoOpsCompleted / (tsInterval / 1000);
918 pEndpoint->tsIntervalStartMs = tsMsCur;
919 pEndpoint->cIoOpsCompleted = 0;
920 }
921#endif
922
923 /* Check whether we can use the per endpoint cache */
924 if ( fLocal
925 && (pEndpoint->cTasksCached < pEndpointClass->cEndpointCacheSize))
926 {
927 /* Add it to the list. */
928 pTask->pPrev = NULL;
929 pEndpoint->pTasksFreeTail->pNext = pTask;
930 pEndpoint->pTasksFreeTail = pTask;
931 ASMAtomicIncU32(&pEndpoint->cTasksCached);
932 }
933 else if (ASMAtomicReadU32(&pEndpoint->cTasksCached) < pEndpointClass->cEpClassCacheSize)
934 {
935 /* Use the global cache. */
936 ASMAtomicIncU32(&pEndpointClass->cTasksCached);
937
938 PPDMASYNCCOMPLETIONTASK pNext;
939 do
940 {
941 pNext = pEndpointClass->apTaskCache[pEndpoint->iSlotStart];
942 pTask->pNext = pNext;
943 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[pEndpoint->iSlotStart], (void *)pTask, (void *)pNext));
944 }
945 else
946 {
947 /* Free it */
948 MMR3HeapFree(pTask);
949 }
950}
951
952static PPDMASYNCCOMPLETIONENDPOINT pdmR3AsyncCompletionFindEndpointWithUri(PPDMASYNCCOMPLETIONEPCLASS pEndpointClass,
953 const char *pszUri)
954{
955 PPDMASYNCCOMPLETIONENDPOINT pEndpoint = pEndpointClass->pEndpointsHead;
956
957 while (pEndpoint)
958 {
959 if (!RTStrCmp(pEndpoint->pszUri, pszUri))
960 return pEndpoint;
961
962 pEndpoint = pEndpoint->pNext;
963 }
964
965 return NULL;
966}
967
968VMMR3DECL(int) PDMR3AsyncCompletionEpCreateForFile(PPPDMASYNCCOMPLETIONENDPOINT ppEndpoint,
969 const char *pszFilename, uint32_t fFlags,
970 PPDMASYNCCOMPLETIONTEMPLATE pTemplate)
971{
972 int rc = VINF_SUCCESS;
973
974 LogFlowFunc((": ppEndpoint=%p pszFilename=%p{%s} fFlags=%u pTemplate=%p\n",
975 ppEndpoint, pszFilename, pszFilename, fFlags, pTemplate));
976
977 /* Sanity checks. */
978 AssertReturn(VALID_PTR(ppEndpoint), VERR_INVALID_POINTER);
979 AssertReturn(VALID_PTR(pszFilename), VERR_INVALID_POINTER);
980 AssertReturn(VALID_PTR(pTemplate), VERR_INVALID_POINTER);
981
982 /* Check that the flags are valid. */
983 AssertReturn(((~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING) & fFlags) == 0),
984 VERR_INVALID_PARAMETER);
985
986 PVM pVM = pTemplate->pVM;
987 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pVM->pdm.s.papAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
988 PPDMASYNCCOMPLETIONENDPOINT pEndpoint = NULL;
989
990 AssertMsg(pEndpointClass, ("File endpoint class was not initialized\n"));
991
992 /* Search for a already opened endpoint for this file. */
993 pEndpoint = pdmR3AsyncCompletionFindEndpointWithUri(pEndpointClass, pszFilename);
994
995 if(!pEndpoint)
996 {
997 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
998 pEndpointClass->pEndpointOps->cbEndpoint,
999 (void **)&pEndpoint);
1000 if (RT_SUCCESS(rc))
1001 {
1002
1003 /* Initialize common parts. */
1004 pEndpoint->pNext = NULL;
1005 pEndpoint->pPrev = NULL;
1006 pEndpoint->pEpClass = pEndpointClass;
1007 pEndpoint->pTasksFreeHead = NULL;
1008 pEndpoint->pTasksFreeTail = NULL;
1009 pEndpoint->cTasksCached = 0;
1010 pEndpoint->uTaskIdNext = 0;
1011 pEndpoint->fTaskIdWraparound = false;
1012 pEndpoint->pTemplate = pTemplate;
1013 pEndpoint->iSlotStart = pEndpointClass->cEndpoints % RT_ELEMENTS(pEndpointClass->apTaskCache);
1014 pEndpoint->pszUri = RTStrDup(pszFilename);
1015 pEndpoint->cUsers = 1;
1016
1017#ifdef VBOX_WITH_STATISTICS
1018 /* Init the statistics part */
1019 for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesNs); i++)
1020 {
1021 rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunTimesNs[i], STAMTYPE_COUNTER,
1022 STAMVISIBILITY_USED,
1023 STAMUNIT_OCCURENCES,
1024 "Nanosecond resolution runtime statistics",
1025 "/PDM/AsyncCompletion/File/%s/TaskRunNs-%u-%u",
1026 RTPathFilename(pEndpoint->pszUri),
1027 i*100, i*100+100-1);
1028 if (RT_FAILURE(rc))
1029 break;
1030 }
1031
1032 if (RT_SUCCESS(rc))
1033 {
1034 for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesMicroSec); i++)
1035 {
1036 rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunTimesMicroSec[i], STAMTYPE_COUNTER,
1037 STAMVISIBILITY_USED,
1038 STAMUNIT_OCCURENCES,
1039 "Microsecond resolution runtime statistics",
1040 "/PDM/AsyncCompletion/File/%s/TaskRunMicroSec-%u-%u",
1041 RTPathFilename(pEndpoint->pszUri),
1042 i*100, i*100+100-1);
1043 if (RT_FAILURE(rc))
1044 break;
1045 }
1046 }
1047
1048 if (RT_SUCCESS(rc))
1049 {
1050 for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesMs); i++)
1051 {
1052 rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunTimesMs[i], STAMTYPE_COUNTER,
1053 STAMVISIBILITY_USED,
1054 STAMUNIT_OCCURENCES,
1055 "Milliseconds resolution runtime statistics",
1056 "/PDM/AsyncCompletion/File/%s/TaskRunMs-%u-%u",
1057 RTPathFilename(pEndpoint->pszUri),
1058 i*100, i*100+100-1);
1059 if (RT_FAILURE(rc))
1060 break;
1061 }
1062 }
1063
1064 if (RT_SUCCESS(rc))
1065 {
1066 for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesMs); i++)
1067 {
1068 rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunTimesSec[i], STAMTYPE_COUNTER,
1069 STAMVISIBILITY_USED,
1070 STAMUNIT_OCCURENCES,
1071 "Second resolution runtime statistics",
1072 "/PDM/AsyncCompletion/File/%s/TaskRunSec-%u-%u",
1073 RTPathFilename(pEndpoint->pszUri),
1074 i*10, i*10+10-1);
1075 if (RT_FAILURE(rc))
1076 break;
1077 }
1078 }
1079
1080 if (RT_SUCCESS(rc))
1081 {
1082 rc = STAMR3RegisterF(pVM, &pEndpoint->StatTaskRunOver100Sec, STAMTYPE_COUNTER,
1083 STAMVISIBILITY_USED,
1084 STAMUNIT_OCCURENCES,
1085 "Tasks which ran more than 100sec",
1086 "/PDM/AsyncCompletion/File/%s/TaskRunSecGreater100Sec",
1087 RTPathFilename(pEndpoint->pszUri));
1088 }
1089
1090 if (RT_SUCCESS(rc))
1091 {
1092 rc = STAMR3RegisterF(pVM, &pEndpoint->StatIoOpsPerSec, STAMTYPE_COUNTER,
1093 STAMVISIBILITY_ALWAYS,
1094 STAMUNIT_OCCURENCES,
1095 "Processed I/O operations per second",
1096 "/PDM/AsyncCompletion/File/%s/IoOpsPerSec",
1097 RTPathFilename(pEndpoint->pszUri));
1098 }
1099
1100 if (RT_SUCCESS(rc))
1101 {
1102 rc = STAMR3RegisterF(pVM, &pEndpoint->StatIoOpsStarted, STAMTYPE_COUNTER,
1103 STAMVISIBILITY_ALWAYS,
1104 STAMUNIT_OCCURENCES,
1105 "Started I/O operations for this endpoint",
1106 "/PDM/AsyncCompletion/File/%s/IoOpsStarted",
1107 RTPathFilename(pEndpoint->pszUri));
1108 }
1109
1110 if (RT_SUCCESS(rc))
1111 {
1112 rc = STAMR3RegisterF(pVM, &pEndpoint->StatIoOpsCompleted, STAMTYPE_COUNTER,
1113 STAMVISIBILITY_ALWAYS,
1114 STAMUNIT_OCCURENCES,
1115 "Completed I/O operations for this endpoint",
1116 "/PDM/AsyncCompletion/File/%s/IoOpsCompleted",
1117 RTPathFilename(pEndpoint->pszUri));
1118 }
1119
1120 pEndpoint->tsIntervalStartMs = RTTimeMilliTS();
1121#endif
1122
1123 if ( pEndpoint->pszUri
1124 && RT_SUCCESS(rc))
1125 {
1126 /* Init the cache. */
1127 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_ASYNC_COMPLETION,
1128 pEndpointClass->pEndpointOps->cbTask,
1129 (void **)&pEndpoint->pTasksFreeHead);
1130 if (RT_SUCCESS(rc))
1131 {
1132 pEndpoint->pTasksFreeTail = pEndpoint->pTasksFreeHead;
1133
1134 /* Call the initializer for the endpoint. */
1135 rc = pEndpointClass->pEndpointOps->pfnEpInitialize(pEndpoint, pszFilename, fFlags);
1136 if (RT_SUCCESS(rc))
1137 {
1138 /* Link it into the list of endpoints. */
1139 rc = RTCritSectEnter(&pEndpointClass->CritSect);
1140 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
1141
1142 pEndpoint->pNext = pEndpointClass->pEndpointsHead;
1143 if (pEndpointClass->pEndpointsHead)
1144 pEndpointClass->pEndpointsHead->pPrev = pEndpoint;
1145
1146 pEndpointClass->pEndpointsHead = pEndpoint;
1147 pEndpointClass->cEndpoints++;
1148
1149 rc = RTCritSectLeave(&pEndpointClass->CritSect);
1150 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
1151
1152 /* Reference the template. */
1153 ASMAtomicIncU32(&pTemplate->cUsed);
1154
1155 *ppEndpoint = pEndpoint;
1156
1157 LogFlowFunc((": Created endpoint for %s: rc=%Rrc\n", pszFilename, rc));
1158 return VINF_SUCCESS;
1159 }
1160 MMR3HeapFree(pEndpoint->pTasksFreeHead);
1161 RTStrFree(pEndpoint->pszUri);
1162 }
1163 else
1164 rc = VERR_NO_MEMORY;
1165 }
1166 MMR3HeapFree(pEndpoint);
1167 }
1168 }
1169 else
1170 {
1171 /* Endpoint found. */
1172 pEndpoint->cUsers++;
1173
1174 *ppEndpoint = pEndpoint;
1175 return VINF_SUCCESS;
1176 }
1177
1178 LogFlowFunc((": Creation of endpoint for %s failed: rc=%Rrc\n", pszFilename, rc));
1179
1180 return rc;
1181}
1182
1183VMMR3DECL(void) PDMR3AsyncCompletionEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1184{
1185 LogFlowFunc((": pEndpoint=%p\n", pEndpoint));
1186
1187 /* Sanity checks. */
1188 AssertReturnVoid(VALID_PTR(pEndpoint));
1189
1190 pEndpoint->cUsers--;
1191
1192 /* If the last user closed the endpoint we will free it. */
1193 if (!pEndpoint->cUsers)
1194 {
1195 PPDMASYNCCOMPLETIONEPCLASS pEndpointClass = pEndpoint->pEpClass;
1196 pEndpointClass->pEndpointOps->pfnEpClose(pEndpoint);
1197
1198 /* Free cached tasks. */
1199 PPDMASYNCCOMPLETIONTASK pTask = pEndpoint->pTasksFreeHead;
1200
1201 while (pTask)
1202 {
1203 PPDMASYNCCOMPLETIONTASK pTaskFree = pTask;
1204 pTask = pTask->pNext;
1205 MMR3HeapFree(pTaskFree);
1206 }
1207
1208 /* Drop reference from the template. */
1209 ASMAtomicDecU32(&pEndpoint->pTemplate->cUsed);
1210
1211 /* Unlink the endpoint from the list. */
1212 int rc = RTCritSectEnter(&pEndpointClass->CritSect);
1213 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
1214
1215 PPDMASYNCCOMPLETIONENDPOINT pEndpointNext = pEndpoint->pNext;
1216 PPDMASYNCCOMPLETIONENDPOINT pEndpointPrev = pEndpoint->pPrev;
1217
1218 if (pEndpointPrev)
1219 pEndpointPrev->pNext = pEndpointNext;
1220 else
1221 pEndpointClass->pEndpointsHead = pEndpointNext;
1222 if (pEndpointNext)
1223 pEndpointNext->pPrev = pEndpointPrev;
1224
1225 pEndpointClass->cEndpoints--;
1226
1227 rc = RTCritSectLeave(&pEndpointClass->CritSect);
1228 AssertMsg(RT_SUCCESS(rc), ("Failed to enter critical section rc=%Rrc\n", rc));
1229
1230#ifdef VBOX_WITH_STATISTICS
1231 /* Deregister the statistics part */
1232 PVM pVM = pEndpointClass->pVM;
1233
1234 for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesNs); i++)
1235 STAMR3Deregister(pVM, &pEndpoint->StatTaskRunTimesNs[i]);
1236 for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesMicroSec); i++)
1237 STAMR3Deregister(pVM, &pEndpoint->StatTaskRunTimesMicroSec[i]);
1238 for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesMs); i++)
1239 STAMR3Deregister(pVM, &pEndpoint->StatTaskRunTimesMs[i]);
1240 for (unsigned i = 0; i < RT_ELEMENTS(pEndpoint->StatTaskRunTimesMs); i++)
1241 STAMR3Deregister(pVM, &pEndpoint->StatTaskRunTimesSec[i]);
1242
1243 STAMR3Deregister(pVM, &pEndpoint->StatTaskRunOver100Sec);
1244 STAMR3Deregister(pVM, &pEndpoint->StatIoOpsPerSec);
1245 STAMR3Deregister(pVM, &pEndpoint->StatIoOpsStarted);
1246 STAMR3Deregister(pVM, &pEndpoint->StatIoOpsCompleted);
1247#endif
1248
1249 RTStrFree(pEndpoint->pszUri);
1250 MMR3HeapFree(pEndpoint);
1251 }
1252}
1253
1254VMMR3DECL(int) PDMR3AsyncCompletionEpRead(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1255 PCPDMDATASEG paSegments, size_t cSegments,
1256 size_t cbRead, void *pvUser,
1257 PPPDMASYNCCOMPLETIONTASK ppTask)
1258{
1259 int rc = VINF_SUCCESS;
1260
1261 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1262 AssertReturn(VALID_PTR(paSegments), VERR_INVALID_POINTER);
1263 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1264 AssertReturn(cSegments > 0, VERR_INVALID_PARAMETER);
1265 AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
1266 AssertReturn(off >= 0, VERR_INVALID_PARAMETER);
1267
1268 PPDMASYNCCOMPLETIONTASK pTask;
1269
1270 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1271 if (!pTask)
1272 return VERR_NO_MEMORY;
1273
1274 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpRead(pTask, pEndpoint, off,
1275 paSegments, cSegments, cbRead);
1276 if (RT_SUCCESS(rc))
1277 {
1278 *ppTask = pTask;
1279 }
1280 else
1281 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1282
1283 return rc;
1284}
1285
1286VMMR3DECL(int) PDMR3AsyncCompletionEpWrite(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1287 PCPDMDATASEG paSegments, size_t cSegments,
1288 size_t cbWrite, void *pvUser,
1289 PPPDMASYNCCOMPLETIONTASK ppTask)
1290{
1291 int rc = VINF_SUCCESS;
1292
1293 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1294 AssertReturn(VALID_PTR(paSegments), VERR_INVALID_POINTER);
1295 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1296 AssertReturn(cSegments > 0, VERR_INVALID_PARAMETER);
1297 AssertReturn(cbWrite > 0, VERR_INVALID_PARAMETER);
1298 AssertReturn(off >= 0, VERR_INVALID_PARAMETER);
1299
1300 PPDMASYNCCOMPLETIONTASK pTask;
1301
1302 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1303 if (!pTask)
1304 return VERR_NO_MEMORY;
1305
1306 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpWrite(pTask, pEndpoint, off,
1307 paSegments, cSegments, cbWrite);
1308 if (RT_SUCCESS(rc))
1309 {
1310 *ppTask = pTask;
1311 }
1312 else
1313 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1314
1315 return rc;
1316}
1317
1318VMMR3DECL(int) PDMR3AsyncCompletionEpFlush(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
1319 void *pvUser,
1320 PPPDMASYNCCOMPLETIONTASK ppTask)
1321{
1322 int rc = VINF_SUCCESS;
1323
1324 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1325 AssertReturn(VALID_PTR(ppTask), VERR_INVALID_POINTER);
1326
1327 PPDMASYNCCOMPLETIONTASK pTask;
1328
1329 pTask = pdmR3AsyncCompletionGetTask(pEndpoint, pvUser);
1330 if (!pTask)
1331 return VERR_NO_MEMORY;
1332
1333 rc = pEndpoint->pEpClass->pEndpointOps->pfnEpFlush(pTask, pEndpoint);
1334 if (RT_SUCCESS(rc))
1335 {
1336 *ppTask = pTask;
1337 }
1338 else
1339 pdmR3AsyncCompletionPutTask(pEndpoint, pTask, false);
1340
1341 return rc;
1342}
1343
1344VMMR3DECL(int) PDMR3AsyncCompletionEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
1345 uint64_t *pcbSize)
1346{
1347 AssertReturn(VALID_PTR(pEndpoint), VERR_INVALID_POINTER);
1348 AssertReturn(VALID_PTR(pcbSize), VERR_INVALID_POINTER);
1349
1350 return pEndpoint->pEpClass->pEndpointOps->pfnEpGetSize(pEndpoint, pcbSize);
1351}
1352
1353VMMR3DECL(int) PDMR3AsyncCompletionTaskCancel(PPDMASYNCCOMPLETIONTASK pTask)
1354{
1355 return VERR_NOT_IMPLEMENTED;
1356}
1357
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