VirtualBox

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

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

PDMNetShaper.cpp: more style.

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