VirtualBox

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

Last change on this file since 90346 was 90346, checked in by vboxsync, 3 years ago
  • VMM: Pass pVM to PDMCritSect APIs. bugref:9218 bugref:10074
  • DrvNetShaper: Do bandwidth allocation via PDMDrvHlp. bugref:10074
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 KB
Line 
1/* $Id: PDMNetShaper.cpp 90346 2021-07-26 19:55:53Z vboxsync $ */
2/** @file
3 * PDM Network Shaper - Limit network traffic according to bandwidth group settings.
4 */
5
6/*
7 * Copyright (C) 2011-2020 Oracle Corporation
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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_NET_SHAPER
23#include "PDMInternal.h"
24#include <VBox/vmm/pdm.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/vm.h>
27#include <VBox/vmm/uvm.h>
28#include <VBox/err.h>
29
30#include <VBox/log.h>
31#include <iprt/asm.h>
32#include <iprt/assert.h>
33#include <iprt/thread.h>
34#include <iprt/mem.h>
35#include <iprt/critsect.h>
36#include <iprt/tcp.h>
37#include <iprt/path.h>
38#include <iprt/string.h>
39
40#include <VBox/vmm/pdmnetshaper.h>
41#include "PDMNetShaperInternal.h"
42
43
44/*********************************************************************************************************************************
45* Structures and Typedefs *
46*********************************************************************************************************************************/
47
48/**
49 * Network shaper data. One instance per VM.
50 */
51typedef struct PDMNETSHAPER
52{
53 /** Pointer to the VM. */
54 PVM pVM;
55 /** Critical section protecting all members below. */
56 RTCRITSECT Lock;
57 /** Pending TX thread. */
58 PPDMTHREAD pTxThread;
59 /** Pointer to the first bandwidth group. */
60 PPDMNSBWGROUP pBwGroupsHead;
61} PDMNETSHAPER;
62
63
64/** Takes the shaper lock (asserts but doesn't return or anything on
65 * failure). */
66#define LOCK_NETSHAPER(a_pShaper) do { int rcShaper = RTCritSectEnter(&(a_pShaper)->Lock); AssertRC(rcShaper); } while (0)
67
68/** Takes the shaper lock, returns + asserts on failure. */
69#define LOCK_NETSHAPER_RETURN(a_pShaper) \
70 do { int rcShaper = RTCritSectEnter(&(a_pShaper)->Lock); AssertRCReturn(rcShaper, rcShaper); } while (0)
71
72/** Releases the shaper lock (asserts on failure). */
73#define UNLOCK_NETSHAPER(a_pShaper) do { int rcShaper = RTCritSectLeave(&(a_pShaper)->Lock); AssertRC(rcShaper); } while (0)
74
75
76
77
78static PPDMNSBWGROUP pdmNsBwGroupFindById(PPDMNETSHAPER pShaper, const char *pszId)
79{
80 PPDMNSBWGROUP pBwGroup = NULL;
81
82 if (RT_VALID_PTR(pszId))
83 {
84 LOCK_NETSHAPER(pShaper);
85
86 pBwGroup = pShaper->pBwGroupsHead;
87 while ( pBwGroup
88 && RTStrCmp(pBwGroup->pszNameR3, pszId))
89 pBwGroup = pBwGroup->pNextR3;
90
91 UNLOCK_NETSHAPER(pShaper);
92 }
93
94 return pBwGroup;
95}
96
97
98static void pdmNsBwGroupLink(PPDMNSBWGROUP pBwGroup)
99{
100 PPDMNETSHAPER pShaper = pBwGroup->pShaperR3;
101 LOCK_NETSHAPER(pShaper);
102
103 pBwGroup->pNextR3 = pShaper->pBwGroupsHead;
104 pShaper->pBwGroupsHead = pBwGroup;
105
106 UNLOCK_NETSHAPER(pShaper);
107}
108
109
110#if 0
111static void pdmNsBwGroupUnlink(PPDMNSBWGROUP pBwGroup)
112{
113 PPDMNETSHAPER pShaper = pBwGroup->pShaper;
114 LOCK_NETSHAPER(pShaper);
115
116 if (pBwGroup == pShaper->pBwGroupsHead)
117 pShaper->pBwGroupsHead = pBwGroup->pNext;
118 else
119 {
120 PPDMNSBWGROUP pPrev = pShaper->pBwGroupsHead;
121 while ( pPrev
122 && pPrev->pNext != pBwGroup)
123 pPrev = pPrev->pNext;
124
125 AssertPtr(pPrev);
126 pPrev->pNext = pBwGroup->pNext;
127 }
128
129 UNLOCK_NETSHAPER(pShaper);
130}
131#endif
132
133
134static void pdmNsBwGroupSetLimit(PPDMNSBWGROUP pBwGroup, uint64_t cbPerSecMax)
135{
136 pBwGroup->cbPerSecMax = cbPerSecMax;
137 pBwGroup->cbBucket = RT_MAX(PDM_NETSHAPER_MIN_BUCKET_SIZE, cbPerSecMax * PDM_NETSHAPER_MAX_LATENCY / 1000);
138 LogFlow(("pdmNsBwGroupSetLimit: New rate limit is %llu bytes per second, adjusted bucket size to %u bytes\n",
139 pBwGroup->cbPerSecMax, pBwGroup->cbBucket));
140}
141
142
143static int pdmNsBwGroupCreate(PPDMNETSHAPER pShaper, const char *pszBwGroup, uint64_t cbPerSecMax)
144{
145 LogFlow(("pdmNsBwGroupCreate: pShaper=%#p pszBwGroup=%#p{%s} cbPerSecMax=%llu\n", pShaper, pszBwGroup, pszBwGroup, cbPerSecMax));
146
147 AssertPtrReturn(pShaper, VERR_INVALID_POINTER);
148 AssertPtrReturn(pszBwGroup, VERR_INVALID_POINTER);
149 AssertReturn(*pszBwGroup != '\0', VERR_INVALID_PARAMETER);
150
151 int rc;
152 PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pszBwGroup);
153 if (!pBwGroup)
154 {
155 rc = MMHyperAlloc(pShaper->pVM, sizeof(PDMNSBWGROUP), 64,
156 MM_TAG_PDM_NET_SHAPER, (void **)&pBwGroup);
157 if (RT_SUCCESS(rc))
158 {
159 rc = PDMR3CritSectInit(pShaper->pVM, &pBwGroup->Lock, RT_SRC_POS, "BWGRP-%s", pszBwGroup);
160 if (RT_SUCCESS(rc))
161 {
162 pBwGroup->pszNameR3 = MMR3HeapStrDup(pShaper->pVM, MM_TAG_PDM_NET_SHAPER, pszBwGroup);
163 if (pBwGroup->pszNameR3)
164 {
165 pBwGroup->pShaperR3 = pShaper;
166 pBwGroup->cRefs = 0;
167
168 pdmNsBwGroupSetLimit(pBwGroup, cbPerSecMax);
169
170 pBwGroup->cbTokensLast = pBwGroup->cbBucket;
171 pBwGroup->tsUpdatedLast = RTTimeSystemNanoTS();
172
173 LogFlowFunc(("pszBwGroup={%s} cbBucket=%u\n",
174 pszBwGroup, pBwGroup->cbBucket));
175 pdmNsBwGroupLink(pBwGroup);
176 return VINF_SUCCESS;
177 }
178 PDMR3CritSectDelete(&pBwGroup->Lock);
179 }
180 MMHyperFree(pShaper->pVM, pBwGroup);
181 }
182 else
183 rc = VERR_NO_MEMORY;
184 }
185 else
186 rc = VERR_ALREADY_EXISTS;
187
188 LogFlowFunc(("returns rc=%Rrc\n", rc));
189 return rc;
190}
191
192
193static void pdmNsBwGroupTerminate(PPDMNSBWGROUP pBwGroup)
194{
195 Assert(pBwGroup->cRefs == 0);
196 if (PDMCritSectIsInitialized(&pBwGroup->Lock))
197 PDMR3CritSectDelete(&pBwGroup->Lock);
198}
199
200
201DECLINLINE(void) pdmNsBwGroupRef(PPDMNSBWGROUP pBwGroup)
202{
203 ASMAtomicIncU32(&pBwGroup->cRefs);
204}
205
206
207DECLINLINE(void) pdmNsBwGroupUnref(PPDMNSBWGROUP pBwGroup)
208{
209 Assert(pBwGroup->cRefs > 0);
210 ASMAtomicDecU32(&pBwGroup->cRefs);
211}
212
213
214static void pdmNsBwGroupXmitPending(PPDMNSBWGROUP pBwGroup)
215{
216 /*
217 * We don't need to hold the bandwidth group lock to iterate over the list
218 * of filters since the filters are removed while the shaper lock is being
219 * held.
220 */
221 AssertPtr(pBwGroup);
222 AssertPtr(pBwGroup->pShaperR3);
223 Assert(RTCritSectIsOwner(&pBwGroup->pShaperR3->Lock));
224 //LOCK_NETSHAPER(pShaper);
225
226 /* Check if the group is disabled. */
227 if (pBwGroup->cbPerSecMax == 0)
228 return;
229
230 PPDMNSFILTER pFilter = pBwGroup->pFiltersHeadR3;
231 while (pFilter)
232 {
233 bool fChoked = ASMAtomicXchgBool(&pFilter->fChoked, false);
234 Log3((LOG_FN_FMT ": pFilter=%#p fChoked=%RTbool\n", __PRETTY_FUNCTION__, pFilter, fChoked));
235 if (fChoked && pFilter->pIDrvNetR3)
236 {
237 LogFlowFunc(("Calling pfnXmitPending for pFilter=%#p\n", pFilter));
238 pFilter->pIDrvNetR3->pfnXmitPending(pFilter->pIDrvNetR3);
239 }
240
241 pFilter = pFilter->pNextR3;
242 }
243
244 //UNLOCK_NETSHAPER(pShaper);
245}
246
247
248static void pdmNsFilterLink(PPDMNSFILTER pFilter)
249{
250 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
251 PVM const pVM = pBwGroup->pShaperR3->pVM;
252 int rc = PDMCritSectEnter(pVM, &pBwGroup->Lock, VERR_SEM_BUSY); AssertRC(rc);
253
254 pFilter->pNextR3 = pBwGroup->pFiltersHeadR3;
255 pBwGroup->pFiltersHeadR3 = pFilter;
256
257 rc = PDMCritSectLeave(pVM, &pBwGroup->Lock); AssertRC(rc);
258}
259
260
261static void pdmNsFilterUnlink(PPDMNSFILTER pFilter)
262{
263 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
264 /*
265 * We need to make sure we hold the shaper lock since pdmNsBwGroupXmitPending()
266 * does not hold the bandwidth group lock while iterating over the list
267 * of group's filters.
268 */
269 AssertPtr(pBwGroup);
270 AssertPtr(pBwGroup->pShaperR3);
271 Assert(RTCritSectIsOwner(&pBwGroup->pShaperR3->Lock));
272 PVM const pVM = pBwGroup->pShaperR3->pVM;
273 int rc = PDMCritSectEnter(pVM, &pBwGroup->Lock, VERR_SEM_BUSY); AssertRC(rc);
274
275 if (pFilter == pBwGroup->pFiltersHeadR3)
276 pBwGroup->pFiltersHeadR3 = pFilter->pNextR3;
277 else
278 {
279 PPDMNSFILTER pPrev = pBwGroup->pFiltersHeadR3;
280 while ( pPrev
281 && pPrev->pNextR3 != pFilter)
282 pPrev = pPrev->pNextR3;
283
284 AssertPtr(pPrev);
285 pPrev->pNextR3 = pFilter->pNextR3;
286 }
287
288 rc = PDMCritSectLeave(pVM, &pBwGroup->Lock); AssertRC(rc);
289}
290
291
292/**
293 * Attach network filter driver from bandwidth group.
294 *
295 * @returns VBox status code.
296 * @param pUVM The user mode VM structure.
297 * @param pDrvIns The driver instance.
298 * @param pszBwGroup Name of the bandwidth group to attach to.
299 * @param pFilter Pointer to the filter we attach.
300 */
301VMMR3_INT_DECL(int) PDMR3NsAttach(PUVM pUVM, PPDMDRVINS pDrvIns, const char *pszBwGroup, PPDMNSFILTER pFilter)
302{
303 VM_ASSERT_EMT(pUVM->pVM);
304 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
305 AssertReturn(pFilter->pBwGroupR3 == NULL, VERR_ALREADY_EXISTS);
306 RT_NOREF_PV(pDrvIns);
307
308 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
309 LOCK_NETSHAPER_RETURN(pShaper);
310
311 int rc = VINF_SUCCESS;
312 PPDMNSBWGROUP pBwGroupNew = NULL;
313 if (pszBwGroup)
314 {
315 pBwGroupNew = pdmNsBwGroupFindById(pShaper, pszBwGroup);
316 if (pBwGroupNew)
317 pdmNsBwGroupRef(pBwGroupNew);
318 else
319 rc = VERR_NOT_FOUND;
320 }
321
322 if (RT_SUCCESS(rc))
323 {
324 PPDMNSBWGROUP pBwGroupOld = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, pBwGroupNew, PPDMNSBWGROUP);
325 ASMAtomicWritePtr(&pFilter->pBwGroupR0, MMHyperR3ToR0(pUVM->pVM, pBwGroupNew));
326 if (pBwGroupOld)
327 pdmNsBwGroupUnref(pBwGroupOld);
328 pdmNsFilterLink(pFilter);
329 }
330
331 UNLOCK_NETSHAPER(pShaper);
332 return rc;
333}
334
335
336/**
337 * Detach network filter driver from bandwidth group.
338 *
339 * @returns VBox status code.
340 * @param pUVM The user mode VM handle.
341 * @param pDrvIns The driver instance.
342 * @param pFilter Pointer to the filter we detach.
343 */
344VMMR3_INT_DECL(int) PDMR3NsDetach(PUVM pUVM, PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
345{
346 RT_NOREF_PV(pDrvIns);
347 VM_ASSERT_EMT(pUVM->pVM);
348 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
349
350 /* Now, return quietly if the filter isn't attached since driver/device
351 destructors are called on constructor failure. */
352 if (!pFilter->pBwGroupR3)
353 return VINF_SUCCESS;
354 AssertPtrReturn(pFilter->pBwGroupR3, VERR_INVALID_POINTER);
355
356 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
357 LOCK_NETSHAPER_RETURN(pShaper);
358
359 pdmNsFilterUnlink(pFilter);
360 PPDMNSBWGROUP pBwGroup = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, NULL, PPDMNSBWGROUP);
361 if (pBwGroup)
362 pdmNsBwGroupUnref(pBwGroup);
363
364 UNLOCK_NETSHAPER(pShaper);
365 return VINF_SUCCESS;
366}
367
368
369/**
370 * Adjusts the maximum rate for the bandwidth group.
371 *
372 * @returns VBox status code.
373 * @param pUVM The user mode VM handle.
374 * @param pszBwGroup Name of the bandwidth group to attach to.
375 * @param cbPerSecMax Maximum number of bytes per second to be transmitted.
376 */
377VMMR3DECL(int) PDMR3NsBwGroupSetLimit(PUVM pUVM, const char *pszBwGroup, uint64_t cbPerSecMax)
378{
379 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
380 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
381 LOCK_NETSHAPER_RETURN(pShaper);
382
383 int rc;
384 PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pszBwGroup);
385 if (pBwGroup)
386 {
387 rc = PDMCritSectEnter(pUVM->pVM, &pBwGroup->Lock, VERR_SEM_BUSY); AssertRC(rc);
388 if (RT_SUCCESS(rc))
389 {
390 pdmNsBwGroupSetLimit(pBwGroup, cbPerSecMax);
391
392 /* Drop extra tokens */
393 if (pBwGroup->cbTokensLast > pBwGroup->cbBucket)
394 pBwGroup->cbTokensLast = pBwGroup->cbBucket;
395
396 int rc2 = PDMCritSectLeave(pUVM->pVM, &pBwGroup->Lock); AssertRC(rc2);
397 }
398 }
399 else
400 rc = VERR_NOT_FOUND;
401
402 UNLOCK_NETSHAPER(pShaper);
403 return rc;
404}
405
406
407/**
408 * I/O thread for pending TX.
409 *
410 * @returns VINF_SUCCESS (ignored).
411 * @param pVM The cross context VM structure.
412 * @param pThread The PDM thread data.
413 */
414static DECLCALLBACK(int) pdmR3NsTxThread(PVM pVM, PPDMTHREAD pThread)
415{
416 RT_NOREF_PV(pVM);
417
418 PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
419 LogFlow(("pdmR3NsTxThread: pShaper=%p\n", pShaper));
420 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
421 {
422 RTThreadSleep(PDM_NETSHAPER_MAX_LATENCY);
423
424 /* Go over all bandwidth groups/filters calling pfnXmitPending */
425 LOCK_NETSHAPER(pShaper);
426 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
427 while (pBwGroup)
428 {
429 pdmNsBwGroupXmitPending(pBwGroup);
430 pBwGroup = pBwGroup->pNextR3;
431 }
432 UNLOCK_NETSHAPER(pShaper);
433 }
434 return VINF_SUCCESS;
435}
436
437
438/**
439 * @copydoc FNPDMTHREADWAKEUPINT
440 */
441static DECLCALLBACK(int) pdmR3NsTxWakeUp(PVM pVM, PPDMTHREAD pThread)
442{
443 RT_NOREF2(pVM, pThread);
444 LogFlow(("pdmR3NsTxWakeUp: pShaper=%p\n", pThread->pvUser));
445 /* Nothing to do */
446 return VINF_SUCCESS;
447}
448
449
450/**
451 * Terminate the network shaper.
452 *
453 * @returns VBox error code.
454 * @param pVM The cross context VM structure.
455 *
456 * @remarks This method destroys all bandwidth group objects.
457 */
458int pdmR3NetShaperTerm(PVM pVM)
459{
460 PUVM pUVM = pVM->pUVM;
461 AssertPtrReturn(pUVM, VERR_INVALID_POINTER);
462 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
463 AssertPtrReturn(pShaper, VERR_INVALID_POINTER);
464
465 /* Destroy the bandwidth managers. */
466 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
467 while (pBwGroup)
468 {
469 PPDMNSBWGROUP pFree = pBwGroup;
470 pBwGroup = pBwGroup->pNextR3;
471 pdmNsBwGroupTerminate(pFree);
472 MMR3HeapFree(pFree->pszNameR3);
473 MMHyperFree(pVM, pFree);
474 }
475
476 RTCritSectDelete(&pShaper->Lock);
477 MMR3HeapFree(pShaper);
478 pUVM->pdm.s.pNetShaper = NULL;
479 return VINF_SUCCESS;
480}
481
482
483/**
484 * Initialize the network shaper.
485 *
486 * @returns VBox status code
487 * @param pVM The cross context VM structure.
488 */
489int pdmR3NetShaperInit(PVM pVM)
490{
491 LogFlow(("pdmR3NetShaperInit: pVM=%p\n", pVM));
492 VM_ASSERT_EMT(pVM);
493 PUVM pUVM = pVM->pUVM;
494 AssertMsgReturn(!pUVM->pdm.s.pNetShaper, ("Network shaper was already initialized\n"), VERR_WRONG_ORDER);
495
496 PPDMNETSHAPER pShaper;
497 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_NET_SHAPER, sizeof(PDMNETSHAPER), (void **)&pShaper);
498 if (RT_SUCCESS(rc))
499 {
500 PCFGMNODE pCfgNetShaper = CFGMR3GetChild(CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM"), "NetworkShaper");
501
502 pShaper->pVM = pVM;
503 rc = RTCritSectInit(&pShaper->Lock);
504 if (RT_SUCCESS(rc))
505 {
506 /* Create all bandwidth groups. */
507 PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNetShaper, "BwGroups");
508 if (pCfgBwGrp)
509 {
510 for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur))
511 {
512 size_t cbName = CFGMR3GetNameLen(pCur) + 1;
513 char *pszBwGrpId = (char *)RTMemAllocZ(cbName);
514 if (pszBwGrpId)
515 {
516 rc = CFGMR3GetName(pCur, pszBwGrpId, cbName);
517 if (RT_SUCCESS(rc))
518 {
519 uint64_t cbMax;
520 rc = CFGMR3QueryU64(pCur, "Max", &cbMax);
521 if (RT_SUCCESS(rc))
522 rc = pdmNsBwGroupCreate(pShaper, pszBwGrpId, cbMax);
523 }
524 RTMemFree(pszBwGrpId);
525 }
526 else
527 rc = VERR_NO_MEMORY;
528 if (RT_FAILURE(rc))
529 break;
530 }
531 }
532
533 if (RT_SUCCESS(rc))
534 {
535 rc = PDMR3ThreadCreate(pVM, &pShaper->pTxThread, pShaper, pdmR3NsTxThread, pdmR3NsTxWakeUp,
536 0 /*cbStack*/, RTTHREADTYPE_IO, "PDMNsTx");
537 if (RT_SUCCESS(rc))
538 {
539 pUVM->pdm.s.pNetShaper = pShaper;
540 return VINF_SUCCESS;
541 }
542 }
543
544 RTCritSectDelete(&pShaper->Lock);
545 }
546
547 MMR3HeapFree(pShaper);
548 }
549
550 LogFlow(("pdmR3NetShaperInit: pVM=%p rc=%Rrc\n", pVM, rc));
551 return rc;
552}
553
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use