VirtualBox

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

Last change on this file since 41783 was 41783, checked in by vboxsync, 12 years ago

Doxygen, comment typos.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 KB
Line 
1/* $Id: PDMNetShaper.cpp 41783 2012-06-16 19:24:15Z 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 uint32_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, uint32_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, uint32_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 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
236
237 PPDMNSFILTER pFilter = pBwGroup->pFiltersHead;
238 while (pFilter)
239 {
240 bool fChoked = ASMAtomicXchgBool(&pFilter->fChoked, false);
241 Log3((LOG_FN_FMT ": pFilter=%#p fChoked=%RTbool\n", __PRETTY_FUNCTION__, pFilter, fChoked));
242 if (fChoked && pFilter->pIDrvNet)
243 {
244 LogFlowFunc(("Calling pfnXmitPending for pFilter=%#p\n", pFilter));
245 pFilter->pIDrvNet->pfnXmitPending(pFilter->pIDrvNet);
246 }
247
248 pFilter = pFilter->pNext;
249 }
250
251 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
252}
253
254static void pdmNsFilterLink(PPDMNSFILTER pFilter)
255{
256 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
257 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
258
259 pFilter->pNext = pBwGroup->pFiltersHead;
260 pBwGroup->pFiltersHead = pFilter;
261
262 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
263}
264
265static void pdmNsFilterUnlink(PPDMNSFILTER pFilter)
266{
267 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
268 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
269
270 if (pFilter == pBwGroup->pFiltersHead)
271 pBwGroup->pFiltersHead = pFilter->pNext;
272 else
273 {
274 PPDMNSFILTER pPrev = pBwGroup->pFiltersHead;
275 while ( pPrev
276 && pPrev->pNext != pFilter)
277 pPrev = pPrev->pNext;
278
279 AssertPtr(pPrev);
280 pPrev->pNext = pFilter->pNext;
281 }
282
283 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
284}
285
286VMMR3DECL(int) PDMR3NsAttach(PVM pVM, PPDMDRVINS pDrvIns, const char *pcszBwGroup,
287 PPDMNSFILTER pFilter)
288{
289 VM_ASSERT_EMT(pVM);
290 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
291 AssertReturn(pFilter->pBwGroupR3 == NULL, VERR_ALREADY_EXISTS);
292
293
294 PUVM pUVM = pVM->pUVM;
295 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
296
297 PPDMNSBWGROUP pBwGroupOld = NULL;
298 PPDMNSBWGROUP pBwGroupNew = NULL;
299
300 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
301 if (RT_SUCCESS(rc))
302 {
303 if (pcszBwGroup)
304 {
305 pBwGroupNew = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
306 if (pBwGroupNew)
307 pdmNsBwGroupRef(pBwGroupNew);
308 else
309 rc = VERR_NOT_FOUND;
310 }
311
312 if (RT_SUCCESS(rc))
313 {
314 pBwGroupOld = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, pBwGroupNew, PPDMNSBWGROUP);
315 if (pBwGroupOld)
316 pdmNsBwGroupUnref(pBwGroupOld);
317 pdmNsFilterLink(pFilter);
318 }
319 int rc2 = RTCritSectLeave(&pShaper->cs); AssertRC(rc2);
320 }
321
322 return rc;
323}
324
325VMMR3DECL(int) PDMR3NsDetach(PVM pVM, PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
326{
327 VM_ASSERT_EMT(pVM);
328 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
329 AssertPtrReturn(pFilter->pBwGroupR3, VERR_INVALID_POINTER);
330
331 PUVM pUVM = pVM->pUVM;
332 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
333
334 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
335 if (RT_SUCCESS(rc))
336 {
337 pdmNsFilterUnlink(pFilter);
338 PPDMNSBWGROUP pBwGroup = NULL;
339 pBwGroup = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, NULL, PPDMNSBWGROUP);
340 if (pBwGroup)
341 pdmNsBwGroupUnref(pBwGroup);
342 int rc2 = RTCritSectLeave(&pShaper->cs); AssertRC(rc2);
343 }
344 return rc;
345}
346
347VMMR3DECL(bool) PDMR3NsAllocateBandwidth(PPDMNSFILTER pFilter, uint32_t cbTransfer)
348{
349 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
350 if (!VALID_PTR(pFilter->pBwGroupR3))
351 return true;
352
353 PPDMNSBWGROUP pBwGroup = ASMAtomicReadPtrT(&pFilter->pBwGroupR3, PPDMNSBWGROUP);
354 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
355 bool fAllowed = true;
356 /* Re-fill the bucket first */
357 uint64_t tsNow = RTTimeSystemNanoTS();
358 uint32_t uTokensAdded = (tsNow - pBwGroup->tsUpdatedLast)*pBwGroup->cbTransferPerSecMax/(1000*1000*1000);
359 uint32_t uTokens = RT_MIN(pBwGroup->cbBucketSize, uTokensAdded + pBwGroup->cbTokensLast);
360
361 if (cbTransfer > uTokens)
362 {
363 fAllowed = false;
364 ASMAtomicWriteBool(&pFilter->fChoked, true);
365 }
366 else
367 {
368 pBwGroup->tsUpdatedLast = tsNow;
369 pBwGroup->cbTokensLast = uTokens - cbTransfer;
370 }
371
372 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
373 Log2((LOG_FN_FMT "BwGroup=%#p{%s} cbTransfer=%u uTokens=%u uTokensAdded=%u fAllowed=%RTbool\n",
374 __PRETTY_FUNCTION__, pBwGroup, pBwGroup->pszName, cbTransfer, uTokens, uTokensAdded, fAllowed));
375 return fAllowed;
376}
377
378VMMR3DECL(int) PDMR3NsBwGroupSetLimit(PVM pVM, const char *pcszBwGroup, uint32_t cbTransferPerSecMax)
379{
380 PUVM pUVM = pVM->pUVM;
381 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
382
383 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
384 if (RT_SUCCESS(rc))
385 {
386 PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
387 if (pBwGroup)
388 {
389 rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
390 pdmNsBwGroupSetLimit(pBwGroup, cbTransferPerSecMax);
391 /* Drop extra tokens */
392 if (pBwGroup->cbTokensLast > pBwGroup->cbBucketSize)
393 pBwGroup->cbTokensLast = pBwGroup->cbBucketSize;
394 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
395 }
396 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
397 }
398 return rc;
399}
400
401
402/**
403 * I/O thread for pending TX.
404 *
405 * @returns VINF_SUCCESS (ignored).
406 * @param pVM Pointer to the VM.
407 * @param pThread The PDM thread data.
408 */
409static DECLCALLBACK(int) pdmR3NsTxThread(PVM pVM, PPDMTHREAD pThread)
410{
411 PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
412 LogFlow(("pdmR3NsTxThread: pShaper=%p\n", pShaper));
413 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
414 {
415 RTThreadSleep(PDM_NETSHAPER_MAX_LATENCY);
416 /* Go over all bandwidth groups/filters calling pfnXmitPending */
417 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
418 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
419 while (pBwGroup)
420 {
421 pdmNsBwGroupXmitPending(pBwGroup);
422 pBwGroup = pBwGroup->pNext;
423 }
424 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
425 }
426 return VINF_SUCCESS;
427}
428
429/**
430 * @copydoc FNPDMTHREADWAKEUPINT
431 */
432static DECLCALLBACK(int) pdmR3NsTxWakeUp(PVM pVM, PPDMTHREAD pThread)
433{
434 PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
435 LogFlow(("pdmR3NsTxWakeUp: pShaper=%p\n", pShaper));
436 /* Nothing to do */
437 return VINF_SUCCESS;
438}
439
440/**
441 * Terminate the network shaper.
442 *
443 * @returns VBox error code.
444 * @param pVM Pointer to VM.
445 *
446 * @remarks This method destroys all bandwidth group objects.
447 */
448int pdmR3NetShaperTerm(PVM pVM)
449{
450 PUVM pUVM = pVM->pUVM;
451 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
452
453 /* Destroy the bandwidth managers. */
454 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
455 while (pBwGroup)
456 {
457 PPDMNSBWGROUP pFree = pBwGroup;
458 pBwGroup = pBwGroup->pNext;
459 pdmNsBwGroupTerminate(pFree);
460 MMR3HeapFree(pFree);
461 }
462
463 RTCritSectDelete(&pShaper->cs);
464 return VINF_SUCCESS;
465}
466
467/**
468 * Initialize the network shaper.
469 *
470 * @returns VBox status code
471 * @param pVM Pointer to the VM.
472 */
473int pdmR3NetShaperInit(PVM pVM)
474{
475 LogFlowFunc((": pVM=%p\n", pVM));
476
477 VM_ASSERT_EMT(pVM);
478
479 PPDMNETSHAPER pNetShaper = NULL;
480
481 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_NET_SHAPER,
482 sizeof(PDMNETSHAPER),
483 (void **)&pNetShaper);
484 if (RT_SUCCESS(rc))
485 {
486 PCFGMNODE pCfgRoot = CFGMR3GetRoot(pVM);
487 PCFGMNODE pCfgNetShaper = CFGMR3GetChild(CFGMR3GetChild(pCfgRoot, "PDM"), "NetworkShaper");
488
489 pNetShaper->pVM = pVM;
490 rc = RTCritSectInit(&pNetShaper->cs);
491 if (RT_SUCCESS(rc))
492 {
493 /* Create all bandwidth groups. */
494 PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNetShaper, "BwGroups");
495
496 if (pCfgBwGrp)
497 {
498 for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur))
499 {
500 uint32_t cbMax;
501 size_t cchName = CFGMR3GetNameLen(pCur) + 1;
502 char *pszBwGrpId = (char *)RTMemAllocZ(cchName);
503
504 if (!pszBwGrpId)
505 {
506 rc = VERR_NO_MEMORY;
507 break;
508 }
509
510 rc = CFGMR3GetName(pCur, pszBwGrpId, cchName);
511 AssertRC(rc);
512
513 if (RT_SUCCESS(rc))
514 rc = CFGMR3QueryU32(pCur, "Max", &cbMax);
515 if (RT_SUCCESS(rc))
516 rc = pdmNsBwGroupCreate(pNetShaper, pszBwGrpId, cbMax);
517
518 RTMemFree(pszBwGrpId);
519
520 if (RT_FAILURE(rc))
521 break;
522 }
523 }
524
525 if (RT_SUCCESS(rc))
526 {
527 PUVM pUVM = pVM->pUVM;
528 AssertMsg(!pUVM->pdm.s.pNetShaper,
529 ("Network shaper was already initialized\n"));
530
531 char szDesc[256];
532 static unsigned iThread;
533
534 RTStrPrintf(szDesc, sizeof(szDesc), "PDMNSTXThread-%d", ++iThread);
535 rc = PDMR3ThreadCreate(pVM, &pNetShaper->hTxThread, pNetShaper,
536 pdmR3NsTxThread, pdmR3NsTxWakeUp, 0,
537 RTTHREADTYPE_IO, szDesc);
538 if (RT_SUCCESS(rc))
539 {
540 pUVM->pdm.s.pNetShaper = pNetShaper;
541 return VINF_SUCCESS;
542 }
543 }
544
545 RTCritSectDelete(&pNetShaper->cs);
546 }
547 MMR3HeapFree(pNetShaper);
548 }
549
550 LogFlowFunc((": pVM=%p rc=%Rrc\n", pVM, rc));
551 return rc;
552}
553
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