VirtualBox

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

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

PCNet,NetShaper: PCNet exact packet len counting + wrong lock order fix in NetShaper (#5582)

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