VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMNetShaper.cpp@ 41932

Last change on this file since 41932 was 41891, checked in by vboxsync, 13 years ago

NetShaper: Fixed segmentation fault caused by limits not fitting in 32-bit integer (#5582)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 KB
Line 
1/* $Id: PDMNetShaper.cpp 41891 2012-06-22 16:56:48Z vboxsync $ */
2/** @file
3 * PDM Network Shaper - Limit network traffic according to bandwidth
4 * group settings.
5 */
6
7/*
8 * Copyright (C) 2006-2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#define LOG_GROUP LOG_GROUP_NET_SHAPER
24#include "PDMInternal.h"
25#include <VBox/vmm/pdm.h>
26#include <VBox/vmm/mm.h>
27#ifdef VBOX_WITH_REM
28# include <VBox/vmm/rem.h>
29#endif
30#include <VBox/vmm/vm.h>
31#include <VBox/vmm/uvm.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#include <iprt/string.h>
43
44#include <VBox/vmm/pdmnetshaper.h>
45
46
47/*******************************************************************************
48* Structures and Typedefs *
49*******************************************************************************/
50
51/**
52 * Bandwidth group instance data
53 */
54typedef struct PDMNSBWGROUP
55{
56 /** Pointer to the next group in the list. */
57 struct PDMNSBWGROUP *pNext;
58 /** Pointer to the shared UVM structure. */
59 struct PDMNETSHAPER *pShaper;
60 /** Critical section protecting all members below. */
61 RTCRITSECT cs;
62 /** Pointer to the first filter attached to this group. */
63 struct PDMNSFILTER *pFiltersHead;
64 /** Bandwidth group name. */
65 char *pszName;
66 /** Maximum number of bytes filters are allowed to transfer. */
67 volatile uint64_t cbTransferPerSecMax;
68 /** Number of bytes we are allowed to transfer in one burst. */
69 volatile uint32_t cbBucketSize;
70 /** Number of bytes we were allowed to transfer at the last update. */
71 volatile uint32_t cbTokensLast;
72 /** Timestamp of the last update */
73 volatile uint64_t tsUpdatedLast;
74 /** Reference counter - How many filters are associated with this group. */
75 volatile uint32_t cRefs;
76} PDMNSBWGROUP;
77/** Pointer to a bandwidth group. */
78typedef PDMNSBWGROUP *PPDMNSBWGROUP;
79
80/**
81 * Network shaper data. One instance per VM.
82 */
83typedef struct PDMNETSHAPER
84{
85 /** Pointer to the VM. */
86 PVM pVM;
87 /** Critical section protecting all members below. */
88 RTCRITSECT cs;
89 /** Pending TX thread. */
90 PPDMTHREAD hTxThread;
91 /** Pointer to the first bandwidth group. */
92 PPDMNSBWGROUP pBwGroupsHead;
93} PDMNETSHAPER;
94
95
96
97/*******************************************************************************
98* Internal Functions *
99*******************************************************************************/
100
101static PPDMNSBWGROUP pdmNsBwGroupFindById(PPDMNETSHAPER pShaper, const char *pcszId)
102{
103 PPDMNSBWGROUP pBwGroup = NULL;
104
105 if (RT_VALID_PTR(pcszId))
106 {
107 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
108
109 pBwGroup = pShaper->pBwGroupsHead;
110 while ( pBwGroup
111 && RTStrCmp(pBwGroup->pszName, pcszId))
112 pBwGroup = pBwGroup->pNext;
113
114 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
115 }
116
117 return pBwGroup;
118}
119
120static void pdmNsBwGroupLink(PPDMNSBWGROUP pBwGroup)
121{
122 PPDMNETSHAPER pShaper = pBwGroup->pShaper;
123 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
124
125 pBwGroup->pNext = pShaper->pBwGroupsHead;
126 pShaper->pBwGroupsHead = pBwGroup;
127
128 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
129}
130
131#if 0
132static void pdmNsBwGroupUnlink(PPDMNSBWGROUP pBwGroup)
133{
134 PPDMNETSHAPER pShaper = pBwGroup->pShaper;
135 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
136
137 if (pBwGroup == pShaper->pBwGroupsHead)
138 pShaper->pBwGroupsHead = pBwGroup->pNext;
139 else
140 {
141 PPDMNSBWGROUP pPrev = pShaper->pBwGroupsHead;
142 while ( pPrev
143 && pPrev->pNext != pBwGroup)
144 pPrev = pPrev->pNext;
145
146 AssertPtr(pPrev);
147 pPrev->pNext = pBwGroup->pNext;
148 }
149
150 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
151}
152#endif
153
154static void pdmNsBwGroupSetLimit(PPDMNSBWGROUP pBwGroup, uint64_t cbTransferPerSecMax)
155{
156 pBwGroup->cbTransferPerSecMax = cbTransferPerSecMax;
157 pBwGroup->cbBucketSize = RT_MAX(PDM_NETSHAPER_MIN_BUCKET_SIZE,
158 cbTransferPerSecMax * PDM_NETSHAPER_MAX_LATENCY / 1000);
159 LogFlowFunc(("New rate limit is %d bytes per second, adjusted bucket size to %d bytes\n",
160 pBwGroup->cbTransferPerSecMax, pBwGroup->cbBucketSize));
161}
162
163static int pdmNsBwGroupCreate(PPDMNETSHAPER pShaper, const char *pcszBwGroup, uint64_t cbTransferPerSecMax)
164{
165 LogFlowFunc(("pShaper=%#p pcszBwGroup=%#p{%s} cbTransferPerSecMax=%u\n",
166 pShaper, pcszBwGroup, pcszBwGroup, cbTransferPerSecMax));
167
168 AssertPtrReturn(pShaper, VERR_INVALID_POINTER);
169 AssertPtrReturn(pcszBwGroup, VERR_INVALID_POINTER);
170 AssertReturn(*pcszBwGroup != '\0', VERR_INVALID_PARAMETER);
171
172 int rc;
173 PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
174 if (!pBwGroup)
175 {
176 rc = MMR3HeapAllocZEx(pShaper->pVM, MM_TAG_PDM_NET_SHAPER,
177 sizeof(PDMNSBWGROUP),
178 (void **)&pBwGroup);
179 if (RT_SUCCESS(rc))
180 {
181 rc = RTCritSectInit(&pBwGroup->cs);
182 if (RT_SUCCESS(rc))
183 {
184 pBwGroup->pszName = RTStrDup(pcszBwGroup);
185 if (pBwGroup->pszName)
186 {
187 pBwGroup->pShaper = pShaper;
188 pBwGroup->cRefs = 0;
189
190 pdmNsBwGroupSetLimit(pBwGroup, cbTransferPerSecMax);
191;
192 pBwGroup->cbTokensLast = pBwGroup->cbBucketSize;
193 pBwGroup->tsUpdatedLast = RTTimeSystemNanoTS();
194
195 LogFlowFunc(("pcszBwGroup={%s} cbBucketSize=%u\n",
196 pcszBwGroup, pBwGroup->cbBucketSize));
197 pdmNsBwGroupLink(pBwGroup);
198 return VINF_SUCCESS;
199 }
200 RTCritSectDelete(&pBwGroup->cs);
201 }
202 MMR3HeapFree(pBwGroup);
203 }
204 else
205 rc = VERR_NO_MEMORY;
206 }
207 else
208 rc = VERR_ALREADY_EXISTS;
209
210 LogFlowFunc(("returns rc=%Rrc\n", rc));
211 return rc;
212}
213
214static void pdmNsBwGroupTerminate(PPDMNSBWGROUP pBwGroup)
215{
216 Assert(pBwGroup->cRefs == 0);
217 if (RTCritSectIsInitialized(&pBwGroup->cs))
218 RTCritSectDelete(&pBwGroup->cs);
219}
220
221
222DECLINLINE(void) pdmNsBwGroupRef(PPDMNSBWGROUP pBwGroup)
223{
224 ASMAtomicIncU32(&pBwGroup->cRefs);
225}
226
227DECLINLINE(void) pdmNsBwGroupUnref(PPDMNSBWGROUP pBwGroup)
228{
229 Assert(pBwGroup->cRefs > 0);
230 ASMAtomicDecU32(&pBwGroup->cRefs);
231}
232
233static void pdmNsBwGroupXmitPending(PPDMNSBWGROUP pBwGroup)
234{
235 /*
236 * We don't need to hold the bandwidth group lock to iterate over the list
237 * of filters since the filters are removed while the shaper lock is being
238 * held.
239 */
240 AssertPtr(pBwGroup);
241 AssertPtr(pBwGroup->pShaper);
242 Assert(RTCritSectIsOwner(&pBwGroup->pShaper->cs));
243 //int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
244
245 /* Check if the group is disabled. */
246 if (pBwGroup->cbTransferPerSecMax == 0)
247 return;
248
249 PPDMNSFILTER pFilter = pBwGroup->pFiltersHead;
250 while (pFilter)
251 {
252 bool fChoked = ASMAtomicXchgBool(&pFilter->fChoked, false);
253 Log3((LOG_FN_FMT ": pFilter=%#p fChoked=%RTbool\n", __PRETTY_FUNCTION__, pFilter, fChoked));
254 if (fChoked && pFilter->pIDrvNet)
255 {
256 LogFlowFunc(("Calling pfnXmitPending for pFilter=%#p\n", pFilter));
257 pFilter->pIDrvNet->pfnXmitPending(pFilter->pIDrvNet);
258 }
259
260 pFilter = pFilter->pNext;
261 }
262
263 //rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
264}
265
266static void pdmNsFilterLink(PPDMNSFILTER pFilter)
267{
268 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
269 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
270
271 pFilter->pNext = pBwGroup->pFiltersHead;
272 pBwGroup->pFiltersHead = pFilter;
273
274 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
275}
276
277static void pdmNsFilterUnlink(PPDMNSFILTER pFilter)
278{
279 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
280 /*
281 * We need to make sure we hold the shaper lock since pdmNsBwGroupXmitPending()
282 * does not hold the bandwidth group lock while iterating over the list
283 * of group's filters.
284 */
285 AssertPtr(pBwGroup);
286 AssertPtr(pBwGroup->pShaper);
287 Assert(RTCritSectIsOwner(&pBwGroup->pShaper->cs));
288 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
289
290 if (pFilter == pBwGroup->pFiltersHead)
291 pBwGroup->pFiltersHead = pFilter->pNext;
292 else
293 {
294 PPDMNSFILTER pPrev = pBwGroup->pFiltersHead;
295 while ( pPrev
296 && pPrev->pNext != pFilter)
297 pPrev = pPrev->pNext;
298
299 AssertPtr(pPrev);
300 pPrev->pNext = pFilter->pNext;
301 }
302
303 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
304}
305
306VMMR3DECL(int) PDMR3NsAttach(PVM pVM, PPDMDRVINS pDrvIns, const char *pcszBwGroup,
307 PPDMNSFILTER pFilter)
308{
309 VM_ASSERT_EMT(pVM);
310 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
311 AssertReturn(pFilter->pBwGroupR3 == NULL, VERR_ALREADY_EXISTS);
312
313
314 PUVM pUVM = pVM->pUVM;
315 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
316
317 PPDMNSBWGROUP pBwGroupOld = NULL;
318 PPDMNSBWGROUP pBwGroupNew = NULL;
319
320 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
321 if (RT_SUCCESS(rc))
322 {
323 if (pcszBwGroup)
324 {
325 pBwGroupNew = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
326 if (pBwGroupNew)
327 pdmNsBwGroupRef(pBwGroupNew);
328 else
329 rc = VERR_NOT_FOUND;
330 }
331
332 if (RT_SUCCESS(rc))
333 {
334 pBwGroupOld = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, pBwGroupNew, PPDMNSBWGROUP);
335 if (pBwGroupOld)
336 pdmNsBwGroupUnref(pBwGroupOld);
337 pdmNsFilterLink(pFilter);
338 }
339 int rc2 = RTCritSectLeave(&pShaper->cs); AssertRC(rc2);
340 }
341
342 return rc;
343}
344
345VMMR3DECL(int) PDMR3NsDetach(PVM pVM, PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
346{
347 VM_ASSERT_EMT(pVM);
348 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
349 AssertPtrReturn(pFilter->pBwGroupR3, VERR_INVALID_POINTER);
350
351 PUVM pUVM = pVM->pUVM;
352 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
353
354 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
355 if (RT_SUCCESS(rc))
356 {
357 pdmNsFilterUnlink(pFilter);
358 PPDMNSBWGROUP pBwGroup = NULL;
359 pBwGroup = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, NULL, PPDMNSBWGROUP);
360 if (pBwGroup)
361 pdmNsBwGroupUnref(pBwGroup);
362 int rc2 = RTCritSectLeave(&pShaper->cs); AssertRC(rc2);
363 }
364 return rc;
365}
366
367VMMR3DECL(bool) PDMR3NsAllocateBandwidth(PPDMNSFILTER pFilter, uint32_t cbTransfer)
368{
369 AssertPtrReturn(pFilter, true);
370 if (!VALID_PTR(pFilter->pBwGroupR3))
371 return true;
372
373 PPDMNSBWGROUP pBwGroup = ASMAtomicReadPtrT(&pFilter->pBwGroupR3, PPDMNSBWGROUP);
374 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
375 bool fAllowed = true;
376 if (pBwGroup->cbTransferPerSecMax)
377 {
378 /* Re-fill the bucket first */
379 uint64_t tsNow = RTTimeSystemNanoTS();
380 uint32_t uTokensAdded = (tsNow - pBwGroup->tsUpdatedLast)*pBwGroup->cbTransferPerSecMax/(1000*1000*1000);
381 uint32_t uTokens = RT_MIN(pBwGroup->cbBucketSize, uTokensAdded + pBwGroup->cbTokensLast);
382
383 if (cbTransfer > uTokens)
384 {
385 fAllowed = false;
386 ASMAtomicWriteBool(&pFilter->fChoked, true);
387 }
388 else
389 {
390 pBwGroup->tsUpdatedLast = tsNow;
391 pBwGroup->cbTokensLast = uTokens - cbTransfer;
392 }
393 Log2((LOG_FN_FMT "BwGroup=%#p{%s} cbTransfer=%u uTokens=%u uTokensAdded=%u fAllowed=%RTbool\n",
394 __PRETTY_FUNCTION__, pBwGroup, pBwGroup->pszName, cbTransfer, uTokens, uTokensAdded, fAllowed));
395 }
396 else
397 Log2((LOG_FN_FMT "BwGroup=%#p{%s} disabled fAllowed=%RTbool\n",
398 __PRETTY_FUNCTION__, pBwGroup, pBwGroup->pszName, fAllowed));
399
400 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
401 return fAllowed;
402}
403
404VMMR3DECL(int) PDMR3NsBwGroupSetLimit(PVM pVM, const char *pcszBwGroup, uint64_t cbTransferPerSecMax)
405{
406 PUVM pUVM = pVM->pUVM;
407 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
408
409 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
410 if (RT_SUCCESS(rc))
411 {
412 PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
413 if (pBwGroup)
414 {
415 rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
416 pdmNsBwGroupSetLimit(pBwGroup, cbTransferPerSecMax);
417 /* Drop extra tokens */
418 if (pBwGroup->cbTokensLast > pBwGroup->cbBucketSize)
419 pBwGroup->cbTokensLast = pBwGroup->cbBucketSize;
420 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
421 }
422 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
423 }
424 return rc;
425}
426
427
428/**
429 * I/O thread for pending TX.
430 *
431 * @returns VINF_SUCCESS (ignored).
432 * @param pVM Pointer to the VM.
433 * @param pThread The PDM thread data.
434 */
435static DECLCALLBACK(int) pdmR3NsTxThread(PVM pVM, PPDMTHREAD pThread)
436{
437 PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
438 LogFlow(("pdmR3NsTxThread: pShaper=%p\n", pShaper));
439 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
440 {
441 RTThreadSleep(PDM_NETSHAPER_MAX_LATENCY);
442 /* Go over all bandwidth groups/filters calling pfnXmitPending */
443 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
444 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
445 while (pBwGroup)
446 {
447 pdmNsBwGroupXmitPending(pBwGroup);
448 pBwGroup = pBwGroup->pNext;
449 }
450 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
451 }
452 return VINF_SUCCESS;
453}
454
455/**
456 * @copydoc FNPDMTHREADWAKEUPINT
457 */
458static DECLCALLBACK(int) pdmR3NsTxWakeUp(PVM pVM, PPDMTHREAD pThread)
459{
460 PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
461 LogFlow(("pdmR3NsTxWakeUp: pShaper=%p\n", pShaper));
462 /* Nothing to do */
463 return VINF_SUCCESS;
464}
465
466/**
467 * Terminate the network shaper.
468 *
469 * @returns VBox error code.
470 * @param pVM Pointer to VM.
471 *
472 * @remarks This method destroys all bandwidth group objects.
473 */
474int pdmR3NetShaperTerm(PVM pVM)
475{
476 PUVM pUVM = pVM->pUVM;
477 AssertPtrReturn(pUVM, VERR_INVALID_POINTER);
478 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
479 AssertPtrReturn(pShaper, VERR_INVALID_POINTER);
480
481 /* Destroy the bandwidth managers. */
482 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
483 while (pBwGroup)
484 {
485 PPDMNSBWGROUP pFree = pBwGroup;
486 pBwGroup = pBwGroup->pNext;
487 pdmNsBwGroupTerminate(pFree);
488 MMR3HeapFree(pFree);
489 }
490
491 RTCritSectDelete(&pShaper->cs);
492 return VINF_SUCCESS;
493}
494
495/**
496 * Initialize the network shaper.
497 *
498 * @returns VBox status code
499 * @param pVM Pointer to the VM.
500 */
501int pdmR3NetShaperInit(PVM pVM)
502{
503 LogFlowFunc((": pVM=%p\n", pVM));
504
505 VM_ASSERT_EMT(pVM);
506
507 PPDMNETSHAPER pNetShaper = NULL;
508
509 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_NET_SHAPER,
510 sizeof(PDMNETSHAPER),
511 (void **)&pNetShaper);
512 if (RT_SUCCESS(rc))
513 {
514 PCFGMNODE pCfgRoot = CFGMR3GetRoot(pVM);
515 PCFGMNODE pCfgNetShaper = CFGMR3GetChild(CFGMR3GetChild(pCfgRoot, "PDM"), "NetworkShaper");
516
517 pNetShaper->pVM = pVM;
518 rc = RTCritSectInit(&pNetShaper->cs);
519 if (RT_SUCCESS(rc))
520 {
521 /* Create all bandwidth groups. */
522 PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNetShaper, "BwGroups");
523
524 if (pCfgBwGrp)
525 {
526 for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur))
527 {
528 uint64_t cbMax;
529 size_t cchName = CFGMR3GetNameLen(pCur) + 1;
530 char *pszBwGrpId = (char *)RTMemAllocZ(cchName);
531
532 if (!pszBwGrpId)
533 {
534 rc = VERR_NO_MEMORY;
535 break;
536 }
537
538 rc = CFGMR3GetName(pCur, pszBwGrpId, cchName);
539 AssertRC(rc);
540
541 if (RT_SUCCESS(rc))
542 rc = CFGMR3QueryU64(pCur, "Max", &cbMax);
543 if (RT_SUCCESS(rc))
544 rc = pdmNsBwGroupCreate(pNetShaper, pszBwGrpId, cbMax);
545
546 RTMemFree(pszBwGrpId);
547
548 if (RT_FAILURE(rc))
549 break;
550 }
551 }
552
553 if (RT_SUCCESS(rc))
554 {
555 PUVM pUVM = pVM->pUVM;
556 AssertMsg(!pUVM->pdm.s.pNetShaper,
557 ("Network shaper was already initialized\n"));
558
559 char szDesc[256];
560 static unsigned iThread;
561
562 RTStrPrintf(szDesc, sizeof(szDesc), "PDMNSTXThread-%d", ++iThread);
563 rc = PDMR3ThreadCreate(pVM, &pNetShaper->hTxThread, pNetShaper,
564 pdmR3NsTxThread, pdmR3NsTxWakeUp, 0,
565 RTTHREADTYPE_IO, szDesc);
566 if (RT_SUCCESS(rc))
567 {
568 pUVM->pdm.s.pNetShaper = pNetShaper;
569 return VINF_SUCCESS;
570 }
571 }
572
573 RTCritSectDelete(&pNetShaper->cs);
574 }
575 MMR3HeapFree(pNetShaper);
576 }
577
578 LogFlowFunc((": pVM=%p rc=%Rrc\n", pVM, rc));
579 return rc;
580}
581
Note: See TracBrowser for help on using the repository browser.

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