VirtualBox

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

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

NetShaper: R0 support (#5582)

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