VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.7 KB
Line 
1/* $Id: PDMNetShaper.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * PDM Network Shaper - Limit network traffic according to bandwidth group settings.
4 */
5
6/*
7 * Copyright (C) 2011-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_NET_SHAPER
33#include <VBox/vmm/pdm.h>
34#include "PDMInternal.h"
35#include <VBox/vmm/vm.h>
36#include <VBox/vmm/uvm.h>
37#include <VBox/err.h>
38
39#include <VBox/log.h>
40#include <iprt/asm.h>
41#include <iprt/assert.h>
42#include <iprt/critsect.h>
43#include <iprt/string.h>
44#include <iprt/semaphore.h>
45#include <iprt/thread.h>
46
47#include <VBox/vmm/pdmnetshaper.h>
48
49
50
51
52/**
53 * Looks up a network bandwidth group by it's name.
54 *
55 * @returns Pointer to the group if found, NULL if not.
56 * @param pVM The cross context VM structure.
57 * @param pszName The name of the group to find.
58 */
59static PPDMNSBWGROUP pdmNsBwGroupFindByName(PVM pVM, const char *pszName)
60{
61 AssertPtrReturn(pszName, NULL);
62 AssertReturn(*pszName != '\0', NULL);
63
64 size_t const cGroups = RT_MIN(pVM->pdm.s.cNsGroups, RT_ELEMENTS(pVM->pdm.s.aNsGroups));
65 for (size_t i = 0; i < cGroups; i++)
66 if (RTStrCmp(pVM->pdm.s.aNsGroups[i].szName, pszName) == 0)
67 return &pVM->pdm.s.aNsGroups[i];
68 return NULL;
69}
70
71
72#ifdef VBOX_STRICT
73/**
74 * Checks if pFilter is attached to the given group by walking the list.
75 */
76DECLINLINE(bool) pdmR3NsIsFilterAttached(PPDMNSBWGROUP pGroup, PPDMNSFILTER pFilter)
77{
78 PPDMNSFILTER pCur;
79 RTListForEach(&pGroup->FilterList, pCur, PDMNSFILTER, ListEntry)
80 {
81 if (pCur == pFilter)
82 return true;
83 }
84 return false;
85}
86#endif
87
88/**
89 * Attaches a network filter driver to the named bandwidth group.
90 *
91 * @returns VBox status code.
92 * @retval VERR_ALREADY_INITIALIZED if already attached.
93 * @retval VERR_NOT_FOUND if the bandwidth wasn't found.
94 *
95 * @param pVM The cross context VM structure.
96 * @param pDrvIns The driver instance.
97 * @param pszName Name of the bandwidth group to attach to.
98 * @param pFilter Pointer to the filter to attach.
99 */
100VMMR3_INT_DECL(int) PDMR3NsAttach(PVM pVM, PPDMDRVINS pDrvIns, const char *pszName, PPDMNSFILTER pFilter)
101{
102 /*
103 * Validate input.
104 */
105 RT_NOREF(pDrvIns);
106 VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
107 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
108
109 uint32_t iGroup = pFilter->iGroup;
110 AssertMsgReturn(iGroup == 0, ("iGroup=%d\n", iGroup), VERR_ALREADY_INITIALIZED);
111 Assert(pFilter->ListEntry.pNext == NULL);
112 Assert(pFilter->ListEntry.pPrev == NULL);
113
114 /* Resolve the group. */
115 PPDMNSBWGROUP pGroup = pdmNsBwGroupFindByName(pVM, pszName);
116 AssertMsgReturn(pGroup, ("'%s'\n", pszName), VERR_NOT_FOUND);
117
118 /*
119 * The attach is protected by PDM::NsLock and by updating iGroup atomatically.
120 */
121 int rc = RTCritSectEnter(&pVM->pdm.s.NsLock);
122 if (RT_SUCCESS(rc))
123 {
124 if (ASMAtomicCmpXchgU32(&pFilter->iGroup, (uint32_t)(pGroup - &pVM->pdm.s.aNsGroups[0]) + 1, 0))
125 {
126 Assert(pFilter->ListEntry.pNext == NULL);
127 Assert(pFilter->ListEntry.pPrev == NULL);
128 RTListAppend(&pGroup->FilterList, &pFilter->ListEntry);
129
130 uint32_t cRefs = ASMAtomicIncU32(&pGroup->cRefs);
131 AssertMsg(cRefs > 0 && cRefs < _16K, ("%u\n", cRefs));
132 RT_NOREF_PV(cRefs);
133
134 LogFlow(("PDMR3NsAttach: Attached '%s'/%u to %s (cRefs=%u)\n",
135 pDrvIns->pReg->szName, pDrvIns->iInstance, pGroup->szName, cRefs));
136 rc = VINF_SUCCESS;
137 }
138 else
139 {
140 AssertMsgFailed(("iGroup=%d (attach race)\n", pFilter->iGroup));
141 rc = VERR_ALREADY_INITIALIZED;
142 }
143
144 int rc2 = RTCritSectLeave(&pVM->pdm.s.NsLock);
145 AssertRC(rc2);
146 }
147
148 return rc;
149}
150
151
152/**
153 * Detaches a network filter driver from its current bandwidth group (if any).
154 *
155 * @returns VBox status code.
156 * @param pVM The cross context VM structure.
157 * @param pDrvIns The driver instance.
158 * @param pFilter Pointer to the filter to detach.
159 */
160VMMR3_INT_DECL(int) PDMR3NsDetach(PVM pVM, PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
161{
162 /*
163 * Validate input.
164 */
165 RT_NOREF(pDrvIns);
166 VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
167 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
168
169 /* Now, return quietly if the filter isn't attached since driver/device
170 destructors are called on constructor failure. */
171 uint32_t const iGroup = ASMAtomicUoReadU32(&pFilter->iGroup);
172 if (!iGroup)
173 return VINF_SUCCESS;
174 AssertMsgReturn(iGroup - 1 < RT_MIN(pVM->pdm.s.cNsGroups, RT_ELEMENTS(pVM->pdm.s.aNsGroups)), ("iGroup=%#x\n", iGroup),
175 VERR_INVALID_HANDLE);
176 PPDMNSBWGROUP const pGroup = &pVM->pdm.s.aNsGroups[iGroup - 1];
177
178 /*
179 * The detaching is protected by PDM::NsLock and by atomically updating iGroup.
180 */
181 int rc = RTCritSectEnter(&pVM->pdm.s.NsLock);
182 if (RT_SUCCESS(rc))
183 {
184 if (ASMAtomicCmpXchgU32(&pFilter->iGroup, 0, iGroup))
185 {
186 Assert(pdmR3NsIsFilterAttached(pGroup, pFilter));
187 RTListNodeRemove(&pFilter->ListEntry);
188 Assert(pFilter->ListEntry.pNext == NULL);
189 Assert(pFilter->ListEntry.pPrev == NULL);
190 ASMAtomicWriteU32(&pFilter->iGroup, 0);
191
192 uint32_t cRefs = ASMAtomicDecU32(&pGroup->cRefs);
193 Assert(cRefs < _16K);
194 RT_NOREF_PV(cRefs);
195
196 LogFlow(("PDMR3NsDetach: Detached '%s'/%u from %s (cRefs=%u)\n",
197 pDrvIns->pReg->szName, pDrvIns->iInstance, pGroup->szName, cRefs));
198 rc = VINF_SUCCESS;
199 }
200 else
201 AssertFailedStmt(rc = VERR_WRONG_ORDER);
202
203 int rc2 = RTCritSectLeave(&pVM->pdm.s.NsLock);
204 AssertRC(rc2);
205 }
206 else
207 AssertRC(rc);
208 return rc;
209}
210
211
212/**
213 * This is used both by pdmR3NsUnchokeThread and PDMR3NsBwGroupSetLimit,
214 * the latter only when setting cbPerSecMax to zero.
215 *
216 * @param pGroup The group which filters should be unchoked.
217 * @note Caller owns the PDM::NsLock critsect.
218 */
219static void pdmR3NsUnchokeGroupFilters(PPDMNSBWGROUP pGroup)
220{
221 PPDMNSFILTER pFilter;
222 RTListForEach(&pGroup->FilterList, pFilter, PDMNSFILTER, ListEntry)
223 {
224 bool fChoked = ASMAtomicXchgBool(&pFilter->fChoked, false);
225 if (fChoked)
226 {
227 PPDMINETWORKDOWN pIDrvNet = pFilter->pIDrvNetR3;
228 if (pIDrvNet && pIDrvNet->pfnXmitPending != NULL)
229 {
230 Log3(("pdmR3NsUnchokeGroupFilters: Unchoked %p in %s, calling %p\n",
231 pFilter, pGroup->szName, pIDrvNet->pfnXmitPending));
232 pIDrvNet->pfnXmitPending(pIDrvNet);
233 }
234 else
235 Log3(("pdmR3NsUnchokeGroupFilters: Unchoked %p in %s (no callback)\n", pFilter, pGroup->szName));
236 }
237 }
238}
239
240
241/**
242 * Worker for PDMR3NsBwGroupSetLimit and pdmR3NetShaperInit.
243 *
244 * @returns New bucket size.
245 * @param pGroup The group to update.
246 * @param cbPerSecMax The new max bytes per second.
247 */
248static uint32_t pdmNsBwGroupSetLimit(PPDMNSBWGROUP pGroup, uint64_t cbPerSecMax)
249{
250 uint32_t const cbRet = RT_MAX(PDM_NETSHAPER_MIN_BUCKET_SIZE, cbPerSecMax * PDM_NETSHAPER_MAX_LATENCY / RT_MS_1SEC);
251 pGroup->cbBucket = cbRet;
252 pGroup->cbPerSecMax = cbPerSecMax;
253 LogFlow(("pdmNsBwGroupSetLimit: New rate limit is %#RX64 bytes per second, adjusted bucket size to %#x bytes\n",
254 cbPerSecMax, cbRet));
255 return cbRet;
256}
257
258
259/**
260 * Adjusts the maximum rate for the bandwidth group.
261 *
262 * @returns VBox status code.
263 * @param pUVM The user mode VM handle.
264 * @param pszName Name of the bandwidth group to attach to.
265 * @param cbPerSecMax Maximum number of bytes per second to be transmitted.
266 */
267VMMR3DECL(int) PDMR3NsBwGroupSetLimit(PUVM pUVM, const char *pszName, uint64_t cbPerSecMax)
268{
269 /*
270 * Validate input.
271 */
272 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
273 PVM const pVM = pUVM->pVM;
274 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
275
276 int rc;
277 PPDMNSBWGROUP pGroup = pdmNsBwGroupFindByName(pVM, pszName);
278 if (pGroup)
279 {
280 /*
281 * Lock the group while we effect the changes.
282 */
283 rc = PDMCritSectEnter(pVM, &pGroup->Lock, VERR_IGNORED);
284 if (RT_SUCCESS(rc))
285 {
286 uint32_t const cbBucket = pdmNsBwGroupSetLimit(pGroup, cbPerSecMax);
287
288 /* Drop extra tokens */
289 if (pGroup->cbTokensLast > cbBucket)
290 pGroup->cbTokensLast = cbBucket;
291 Log(("PDMR3NsBwGroupSetLimit/%s: cbBucket=%#x cbPerSecMax=%#RX64\n", pGroup->szName, cbBucket, cbPerSecMax));
292
293 int rc2 = PDMCritSectLeave(pVM, &pGroup->Lock);
294 AssertRC(rc2);
295
296 /*
297 * If we disabled the group, we must make sure to unchoke all filter
298 * as the thread will ignore the group from now on.
299 *
300 * We do this after leaving the group lock to keep the locking simple.
301 * Extra pfnXmitPending calls should be harmless, of course ASSUMING
302 * nobody take offence to being called on this thread.
303 */
304 if (cbPerSecMax == 0)
305 {
306 Log(("PDMR3NsBwGroupSetLimit: cbPerSecMax was set to zero, so unchoking filters...\n"));
307 rc = RTCritSectEnter(&pVM->pdm.s.NsLock);
308 AssertRC(rc);
309
310 pdmR3NsUnchokeGroupFilters(pGroup);
311
312 rc2 = RTCritSectLeave(&pVM->pdm.s.NsLock);
313 AssertRC(rc2);
314 }
315 }
316 else
317 AssertRC(rc);
318 }
319 else
320 rc = VERR_NOT_FOUND;
321 return rc;
322}
323
324
325/**
326 * I/O thread for pending unchoking and associating transmitting.
327 *
328 * @returns VINF_SUCCESS (ignored).
329 * @param pVM The cross context VM structure.
330 * @param pThread The PDM thread data.
331 */
332static DECLCALLBACK(int) pdmR3NsUnchokeThread(PVM pVM, PPDMTHREAD pThread)
333{
334 LogFlow(("pdmR3NsUnchokeThread: pVM=%p\n", pVM));
335 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
336 {
337 int rc = RTSemEventWait(pVM->pdm.s.hNsUnchokeEvt, RT_INDEFINITE_WAIT);
338 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
339 break;
340 AssertMsgStmt(RT_SUCCESS(rc) || rc == VERR_TIMEOUT /* paranioa*/, ("%Rrc\n", rc),
341 RTThreadSleep(PDM_NETSHAPER_MAX_LATENCY));
342
343 /*
344 * Go over all bandwidth groups/filters and unchoke their filters.
345 *
346 * We take the main lock here to prevent any detaching or attaching
347 * from taking place while we're traversing the filter lists.
348 */
349 rc = RTCritSectEnter(&pVM->pdm.s.NsLock);
350 AssertRC(rc);
351
352 size_t const cGroups = RT_MIN(pVM->pdm.s.cNsGroups, RT_ELEMENTS(pVM->pdm.s.aNsGroups));
353 for (size_t i = 0; i < cGroups; i++)
354 {
355 PPDMNSBWGROUP const pGroup = &pVM->pdm.s.aNsGroups[i];
356 if ( pGroup->cRefs > 0
357 && pGroup->cbPerSecMax > 0)
358 pdmR3NsUnchokeGroupFilters(pGroup);
359 }
360
361 rc = RTCritSectLeave(&pVM->pdm.s.NsLock);
362 AssertRC(rc);
363 }
364 return VINF_SUCCESS;
365}
366
367
368/**
369 * @copydoc FNPDMTHREADWAKEUPINT
370 */
371static DECLCALLBACK(int) pdmR3NsUnchokeWakeUp(PVM pVM, PPDMTHREAD pThread)
372{
373 LogFlow(("pdmR3NsUnchokeWakeUp:\n"));
374
375 /* Wake up the thread. */
376 int rc = RTSemEventSignal(pVM->pdm.s.hNsUnchokeEvt);
377 AssertRC(rc);
378
379 RT_NOREF(pThread);
380 return VINF_SUCCESS;
381}
382
383
384/**
385 * @callback_method_impl{FNTMTIMERINT, Wakes up pdmR3NsUnchokeThread.}
386 */
387static DECLCALLBACK(void) pdmR3NsUnchokeTimer(PVM pVM, TMTIMERHANDLE hTimer, void *pvUser)
388{
389 ASMAtomicWriteBool(&pVM->pdm.s.fNsUnchokeTimerArmed, false);
390
391 /* Wake up the thread. */
392 int rc = RTSemEventSignal(pVM->pdm.s.hNsUnchokeEvt);
393 AssertRC(rc);
394
395 RT_NOREF(hTimer, pvUser);
396}
397
398
399/**
400 * Terminate the network shaper, groups, lock and everything.
401 *
402 * @param pVM The cross context VM structure.
403 */
404void pdmR3NetShaperTerm(PVM pVM)
405{
406 size_t const cGroups = RT_MIN(pVM->pdm.s.cNsGroups, RT_ELEMENTS(pVM->pdm.s.aNsGroups));
407 for (size_t i = 0; i < cGroups; i++)
408 {
409 PPDMNSBWGROUP const pGroup = &pVM->pdm.s.aNsGroups[i];
410 AssertMsg(pGroup->cRefs == 0, ("cRefs=%s '%s'\n", pGroup->cRefs, pGroup->szName));
411 AssertContinue(PDMCritSectIsInitialized(&pGroup->Lock));
412 PDMR3CritSectDelete(pVM, &pGroup->Lock);
413 }
414
415 RTCritSectDelete(&pVM->pdm.s.NsLock);
416}
417
418
419/**
420 * Initialize the network shaper.
421 *
422 * @returns VBox status code
423 * @param pVM The cross context VM structure.
424 */
425int pdmR3NetShaperInit(PVM pVM)
426{
427 LogFlow(("pdmR3NetShaperInit: pVM=%p\n", pVM));
428 VM_ASSERT_EMT(pVM);
429
430 Assert(pVM->pdm.s.cNsGroups == 0);
431 pVM->pdm.s.hNsUnchokeEvt = NIL_RTSEMEVENT;
432 pVM->pdm.s.hNsUnchokeTimer = NIL_TMTIMERHANDLE;
433
434 /*
435 * Initialize the critical section protecting attaching, detaching and unchoking.
436 *
437 * This is a non-recursive lock to make sure nobody tries to mess with the groups
438 * from the pfnXmitPending callback.
439 */
440 int rc = RTCritSectInitEx(&pVM->pdm.s.NsLock, RTCRITSECT_FLAGS_NO_NESTING,
441 NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "PDMNetShaper");
442 AssertRCReturn(rc, rc);
443
444 /*
445 * Initialize all bandwidth groups.
446 */
447 PCFGMNODE pCfgNetShaper = CFGMR3GetChild(CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM"), "NetworkShaper");
448 PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNetShaper, "BwGroups");
449 if (pCfgBwGrp)
450 {
451 uint32_t iGroup = 0;
452 for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur))
453 {
454 /*
455 * Get the config data.
456 */
457 size_t cchName = CFGMR3GetNameLen(pCur);
458 AssertBreakStmt(cchName <= PDM_NET_SHAPER_MAX_NAME_LEN,
459 rc = VMR3SetError(pVM->pUVM, VERR_INVALID_NAME, RT_SRC_POS,
460 N_("Network shaper group name #%u is too long: %zu, max %u"),
461 iGroup, cchName, PDM_NET_SHAPER_MAX_NAME_LEN));
462 char szName[PDM_NET_SHAPER_MAX_NAME_LEN + 1];
463 rc = CFGMR3GetName(pCur, szName, sizeof(szName));
464 AssertRCBreak(rc);
465 AssertBreakStmt(szName[0] != '\0',
466 rc = VMR3SetError(pVM->pUVM, VERR_INVALID_NAME, RT_SRC_POS,
467 N_("Empty network shaper group name #%u"), iGroup));
468
469 uint64_t cbMax;
470 rc = CFGMR3QueryU64(pCur, "Max", &cbMax);
471 AssertRCBreakStmt(rc, rc = VMR3SetError(pVM->pUVM, rc, RT_SRC_POS,
472 N_("Failed to read 'Max' value for network shaper group '%s': %Rrc"),
473 szName, rc));
474
475 /*
476 * Initialize the group table entry.
477 */
478 AssertBreakStmt(iGroup < RT_ELEMENTS(pVM->pdm.s.aNsGroups),
479 rc = VMR3SetError(pVM->pUVM, VERR_TOO_MUCH_DATA, RT_SRC_POS, N_("Too many bandwidth groups (max %zu)"),
480 RT_ELEMENTS(pVM->pdm.s.aNsGroups)));
481
482 rc = PDMR3CritSectInit(pVM, &pVM->pdm.s.aNsGroups[iGroup].Lock, RT_SRC_POS, "BWGRP%02u-%s", iGroup, szName);
483 AssertRCBreak(rc);
484
485 RTListInit(&pVM->pdm.s.aNsGroups[iGroup].FilterList);
486 pVM->pdm.s.aNsGroups[iGroup].cRefs = 0;
487 RTStrCopy(pVM->pdm.s.aNsGroups[iGroup].szName, sizeof(pVM->pdm.s.aNsGroups[iGroup].szName), szName);
488 pVM->pdm.s.aNsGroups[iGroup].cbTokensLast = pdmNsBwGroupSetLimit(&pVM->pdm.s.aNsGroups[iGroup], cbMax);
489 pVM->pdm.s.aNsGroups[iGroup].tsUpdatedLast = RTTimeSystemNanoTS();
490 LogFlowFunc(("PDM NetShaper Group #%u: %s - cbPerSecMax=%#RU64 cbBucket=%#x\n",
491 iGroup, pVM->pdm.s.aNsGroups[iGroup].szName, pVM->pdm.s.aNsGroups[iGroup].cbPerSecMax,
492 pVM->pdm.s.aNsGroups[iGroup].cbBucket));
493
494 /*
495 * Register statistics.
496 */
497 STAMR3RegisterF(pVM, (void *)&pVM->pdm.s.aNsGroups[iGroup].cbPerSecMax, STAMTYPE_U64, STAMVISIBILITY_ALWAYS,
498 STAMUNIT_BYTES, "", "/PDM/NetShaper/%u-%s/cbPerSecMax", iGroup, szName);
499 STAMR3RegisterF(pVM, (void *)&pVM->pdm.s.aNsGroups[iGroup].cRefs, STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
500 STAMUNIT_BYTES, "", "/PDM/NetShaper/%u-%s/cRefs", iGroup, szName);
501 STAMR3RegisterF(pVM, (void *)&pVM->pdm.s.aNsGroups[iGroup].cbBucket, STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
502 STAMUNIT_BYTES, "", "/PDM/NetShaper/%u-%s/cbBucket", iGroup, szName);
503 STAMR3RegisterF(pVM, (void *)&pVM->pdm.s.aNsGroups[iGroup].cbTokensLast, STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
504 STAMUNIT_BYTES, "", "/PDM/NetShaper/%u-%s/cbTokensLast", iGroup, szName);
505 STAMR3RegisterF(pVM, (void *)&pVM->pdm.s.aNsGroups[iGroup].tsUpdatedLast, STAMTYPE_U64, STAMVISIBILITY_ALWAYS,
506 STAMUNIT_NS, "", "/PDM/NetShaper/%u-%s/tsUpdatedLast", iGroup, szName);
507 STAMR3RegisterF(pVM, (void *)&pVM->pdm.s.aNsGroups[iGroup].cTotalChokings, STAMTYPE_U64_RESET, STAMVISIBILITY_ALWAYS,
508 STAMUNIT_OCCURENCES, "", "/PDM/NetShaper/%u-%s/TotalChokings", iGroup, szName);
509
510 pVM->pdm.s.cNsGroups = ++iGroup;
511 }
512 }
513 if (RT_SUCCESS(rc))
514 {
515 /*
516 * If there are any groups configured, create a unchoke thread and an
517 * associated timer for waking it up when needed. The timer runs on
518 * the real time clock.
519 */
520 if (pVM->pdm.s.cNsGroups == 0)
521 {
522 LogFlowFunc(("returns VINF_SUCCESS - no groups\n"));
523 return VINF_SUCCESS;
524 }
525
526 rc = RTSemEventCreate(&pVM->pdm.s.hNsUnchokeEvt);
527 if (RT_SUCCESS(rc))
528 {
529 rc = TMR3TimerCreate(pVM, TMCLOCK_REAL, pdmR3NsUnchokeTimer, NULL, TMTIMER_FLAGS_NO_RING0,
530 "PDMNetShaperUnchoke", &pVM->pdm.s.hNsUnchokeTimer);
531 if (RT_SUCCESS(rc))
532 {
533 rc = PDMR3ThreadCreate(pVM, &pVM->pdm.s.pNsUnchokeThread, NULL, pdmR3NsUnchokeThread, pdmR3NsUnchokeWakeUp,
534 0 /*cbStack*/, RTTHREADTYPE_IO, "PDMNsUnchoke");
535 if (RT_SUCCESS(rc))
536 {
537
538 LogFlowFunc(("returns VINF_SUCCESS (%u groups)\n", pVM->pdm.s.cNsGroups));
539 return VINF_SUCCESS;
540 }
541 }
542 }
543 }
544
545 RTCritSectDelete(&pVM->pdm.s.NsLock);
546 LogRel(("pdmR3NetShaperInit: failed rc=%Rrc\n", rc));
547 return rc;
548}
549
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