VirtualBox

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

Last change on this file since 80239 was 80191, checked in by vboxsync, 5 years ago

VMM/r3: Refactored VMCPU enumeration in preparation that aCpus will be replaced with a pointer array. Removed two raw-mode offset members from the CPUM and CPUMCPU sub-structures. bugref:9217 bugref:9517

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