VirtualBox

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

Last change on this file since 86649 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.8 KB
Line 
1/* $Id: PDMNetShaper.cpp 82968 2020-02-04 10:35:17Z 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 int rc = PDMCritSectEnter(&pBwGroup->Lock, VERR_SEM_BUSY); AssertRC(rc);
252
253 pFilter->pNextR3 = pBwGroup->pFiltersHeadR3;
254 pBwGroup->pFiltersHeadR3 = pFilter;
255
256 rc = PDMCritSectLeave(&pBwGroup->Lock); AssertRC(rc);
257}
258
259
260static void pdmNsFilterUnlink(PPDMNSFILTER pFilter)
261{
262 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
263 /*
264 * We need to make sure we hold the shaper lock since pdmNsBwGroupXmitPending()
265 * does not hold the bandwidth group lock while iterating over the list
266 * of group's filters.
267 */
268 AssertPtr(pBwGroup);
269 AssertPtr(pBwGroup->pShaperR3);
270 Assert(RTCritSectIsOwner(&pBwGroup->pShaperR3->Lock));
271 int rc = PDMCritSectEnter(&pBwGroup->Lock, VERR_SEM_BUSY); AssertRC(rc);
272
273 if (pFilter == pBwGroup->pFiltersHeadR3)
274 pBwGroup->pFiltersHeadR3 = pFilter->pNextR3;
275 else
276 {
277 PPDMNSFILTER pPrev = pBwGroup->pFiltersHeadR3;
278 while ( pPrev
279 && pPrev->pNextR3 != pFilter)
280 pPrev = pPrev->pNextR3;
281
282 AssertPtr(pPrev);
283 pPrev->pNextR3 = pFilter->pNextR3;
284 }
285
286 rc = PDMCritSectLeave(&pBwGroup->Lock); AssertRC(rc);
287}
288
289
290/**
291 * Attach network filter driver from bandwidth group.
292 *
293 * @returns VBox status code.
294 * @param pUVM The user mode VM structure.
295 * @param pDrvIns The driver instance.
296 * @param pszBwGroup Name of the bandwidth group to attach to.
297 * @param pFilter Pointer to the filter we attach.
298 */
299VMMR3_INT_DECL(int) PDMR3NsAttach(PUVM pUVM, PPDMDRVINS pDrvIns, const char *pszBwGroup, PPDMNSFILTER pFilter)
300{
301 VM_ASSERT_EMT(pUVM->pVM);
302 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
303 AssertReturn(pFilter->pBwGroupR3 == NULL, VERR_ALREADY_EXISTS);
304 RT_NOREF_PV(pDrvIns);
305
306 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
307 LOCK_NETSHAPER_RETURN(pShaper);
308
309 int rc = VINF_SUCCESS;
310 PPDMNSBWGROUP pBwGroupNew = NULL;
311 if (pszBwGroup)
312 {
313 pBwGroupNew = pdmNsBwGroupFindById(pShaper, pszBwGroup);
314 if (pBwGroupNew)
315 pdmNsBwGroupRef(pBwGroupNew);
316 else
317 rc = VERR_NOT_FOUND;
318 }
319
320 if (RT_SUCCESS(rc))
321 {
322 PPDMNSBWGROUP pBwGroupOld = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, pBwGroupNew, PPDMNSBWGROUP);
323 ASMAtomicWritePtr(&pFilter->pBwGroupR0, MMHyperR3ToR0(pUVM->pVM, pBwGroupNew));
324 if (pBwGroupOld)
325 pdmNsBwGroupUnref(pBwGroupOld);
326 pdmNsFilterLink(pFilter);
327 }
328
329 UNLOCK_NETSHAPER(pShaper);
330 return rc;
331}
332
333
334/**
335 * Detach network filter driver from bandwidth group.
336 *
337 * @returns VBox status code.
338 * @param pUVM The user mode VM handle.
339 * @param pDrvIns The driver instance.
340 * @param pFilter Pointer to the filter we detach.
341 */
342VMMR3_INT_DECL(int) PDMR3NsDetach(PUVM pUVM, PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
343{
344 RT_NOREF_PV(pDrvIns);
345 VM_ASSERT_EMT(pUVM->pVM);
346 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
347
348 /* Now, return quietly if the filter isn't attached since driver/device
349 destructors are called on constructor failure. */
350 if (!pFilter->pBwGroupR3)
351 return VINF_SUCCESS;
352 AssertPtrReturn(pFilter->pBwGroupR3, VERR_INVALID_POINTER);
353
354 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
355 LOCK_NETSHAPER_RETURN(pShaper);
356
357 pdmNsFilterUnlink(pFilter);
358 PPDMNSBWGROUP pBwGroup = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, NULL, PPDMNSBWGROUP);
359 if (pBwGroup)
360 pdmNsBwGroupUnref(pBwGroup);
361
362 UNLOCK_NETSHAPER(pShaper);
363 return VINF_SUCCESS;
364}
365
366
367/**
368 * Adjusts the maximum rate for the bandwidth group.
369 *
370 * @returns VBox status code.
371 * @param pUVM The user mode VM handle.
372 * @param pszBwGroup Name of the bandwidth group to attach to.
373 * @param cbPerSecMax Maximum number of bytes per second to be transmitted.
374 */
375VMMR3DECL(int) PDMR3NsBwGroupSetLimit(PUVM pUVM, const char *pszBwGroup, uint64_t cbPerSecMax)
376{
377 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
378 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
379 LOCK_NETSHAPER_RETURN(pShaper);
380
381 int rc;
382 PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pszBwGroup);
383 if (pBwGroup)
384 {
385 rc = PDMCritSectEnter(&pBwGroup->Lock, VERR_SEM_BUSY); AssertRC(rc);
386 if (RT_SUCCESS(rc))
387 {
388 pdmNsBwGroupSetLimit(pBwGroup, cbPerSecMax);
389
390 /* Drop extra tokens */
391 if (pBwGroup->cbTokensLast > pBwGroup->cbBucket)
392 pBwGroup->cbTokensLast = pBwGroup->cbBucket;
393
394 int rc2 = PDMCritSectLeave(&pBwGroup->Lock); AssertRC(rc2);
395 }
396 }
397 else
398 rc = VERR_NOT_FOUND;
399
400 UNLOCK_NETSHAPER(pShaper);
401 return rc;
402}
403
404
405/**
406 * I/O thread for pending TX.
407 *
408 * @returns VINF_SUCCESS (ignored).
409 * @param pVM The cross context VM structure.
410 * @param pThread The PDM thread data.
411 */
412static DECLCALLBACK(int) pdmR3NsTxThread(PVM pVM, PPDMTHREAD pThread)
413{
414 RT_NOREF_PV(pVM);
415
416 PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
417 LogFlow(("pdmR3NsTxThread: pShaper=%p\n", pShaper));
418 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
419 {
420 RTThreadSleep(PDM_NETSHAPER_MAX_LATENCY);
421
422 /* Go over all bandwidth groups/filters calling pfnXmitPending */
423 LOCK_NETSHAPER(pShaper);
424 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
425 while (pBwGroup)
426 {
427 pdmNsBwGroupXmitPending(pBwGroup);
428 pBwGroup = pBwGroup->pNextR3;
429 }
430 UNLOCK_NETSHAPER(pShaper);
431 }
432 return VINF_SUCCESS;
433}
434
435
436/**
437 * @copydoc FNPDMTHREADWAKEUPINT
438 */
439static DECLCALLBACK(int) pdmR3NsTxWakeUp(PVM pVM, PPDMTHREAD pThread)
440{
441 RT_NOREF2(pVM, pThread);
442 LogFlow(("pdmR3NsTxWakeUp: pShaper=%p\n", pThread->pvUser));
443 /* Nothing to do */
444 return VINF_SUCCESS;
445}
446
447
448/**
449 * Terminate the network shaper.
450 *
451 * @returns VBox error code.
452 * @param pVM The cross context VM structure.
453 *
454 * @remarks This method destroys all bandwidth group objects.
455 */
456int pdmR3NetShaperTerm(PVM pVM)
457{
458 PUVM pUVM = pVM->pUVM;
459 AssertPtrReturn(pUVM, VERR_INVALID_POINTER);
460 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
461 AssertPtrReturn(pShaper, VERR_INVALID_POINTER);
462
463 /* Destroy the bandwidth managers. */
464 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
465 while (pBwGroup)
466 {
467 PPDMNSBWGROUP pFree = pBwGroup;
468 pBwGroup = pBwGroup->pNextR3;
469 pdmNsBwGroupTerminate(pFree);
470 MMR3HeapFree(pFree->pszNameR3);
471 MMHyperFree(pVM, pFree);
472 }
473
474 RTCritSectDelete(&pShaper->Lock);
475 MMR3HeapFree(pShaper);
476 pUVM->pdm.s.pNetShaper = NULL;
477 return VINF_SUCCESS;
478}
479
480
481/**
482 * Initialize the network shaper.
483 *
484 * @returns VBox status code
485 * @param pVM The cross context VM structure.
486 */
487int pdmR3NetShaperInit(PVM pVM)
488{
489 LogFlow(("pdmR3NetShaperInit: pVM=%p\n", pVM));
490 VM_ASSERT_EMT(pVM);
491 PUVM pUVM = pVM->pUVM;
492 AssertMsgReturn(!pUVM->pdm.s.pNetShaper, ("Network shaper was already initialized\n"), VERR_WRONG_ORDER);
493
494 PPDMNETSHAPER pShaper;
495 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_NET_SHAPER, sizeof(PDMNETSHAPER), (void **)&pShaper);
496 if (RT_SUCCESS(rc))
497 {
498 PCFGMNODE pCfgNetShaper = CFGMR3GetChild(CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM"), "NetworkShaper");
499
500 pShaper->pVM = pVM;
501 rc = RTCritSectInit(&pShaper->Lock);
502 if (RT_SUCCESS(rc))
503 {
504 /* Create all bandwidth groups. */
505 PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNetShaper, "BwGroups");
506 if (pCfgBwGrp)
507 {
508 for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur))
509 {
510 size_t cbName = CFGMR3GetNameLen(pCur) + 1;
511 char *pszBwGrpId = (char *)RTMemAllocZ(cbName);
512 if (pszBwGrpId)
513 {
514 rc = CFGMR3GetName(pCur, pszBwGrpId, cbName);
515 if (RT_SUCCESS(rc))
516 {
517 uint64_t cbMax;
518 rc = CFGMR3QueryU64(pCur, "Max", &cbMax);
519 if (RT_SUCCESS(rc))
520 rc = pdmNsBwGroupCreate(pShaper, pszBwGrpId, cbMax);
521 }
522 RTMemFree(pszBwGrpId);
523 }
524 else
525 rc = VERR_NO_MEMORY;
526 if (RT_FAILURE(rc))
527 break;
528 }
529 }
530
531 if (RT_SUCCESS(rc))
532 {
533 rc = PDMR3ThreadCreate(pVM, &pShaper->pTxThread, pShaper, pdmR3NsTxThread, pdmR3NsTxWakeUp,
534 0 /*cbStack*/, RTTHREADTYPE_IO, "PDMNsTx");
535 if (RT_SUCCESS(rc))
536 {
537 pUVM->pdm.s.pNetShaper = pShaper;
538 return VINF_SUCCESS;
539 }
540 }
541
542 RTCritSectDelete(&pShaper->Lock);
543 }
544
545 MMR3HeapFree(pShaper);
546 }
547
548 LogFlow(("pdmR3NetShaperInit: pVM=%p rc=%Rrc\n", pVM, rc));
549 return rc;
550}
551
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