VirtualBox

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

Last change on this file since 50653 was 45061, checked in by vboxsync, 11 years ago

Review of PDM driver destructors making sure that variables they use are correctly initialized in the constructor. Found several RTFileClose(0) cases.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.4 KB
Line 
1/* $Id: PDMNetShaper.cpp 45061 2013-03-18 14:09:03Z vboxsync $ */
2/** @file
3 * PDM Network Shaper - Limit network traffic according to bandwidth group settings.
4 */
5
6/*
7 * Copyright (C) 2011-2013 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 "PDMNetShaperInternal.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 Lock;
60 /** Pending TX thread. */
61 PPDMTHREAD pTxThread;
62 /** Pointer to the first bandwidth group. */
63 PPDMNSBWGROUP pBwGroupsHead;
64} PDMNETSHAPER;
65
66
67/** Takes the shaper lock (asserts but doesn't return or anything on
68 * failure). */
69#define LOCK_NETSHAPER(a_pShaper) do { int rcShaper = RTCritSectEnter(&(a_pShaper)->Lock); AssertRC(rcShaper); } while (0)
70
71/** Takes the shaper lock, returns + asserts on failure. */
72#define LOCK_NETSHAPER_RETURN(a_pShaper) \
73 do { int rcShaper = RTCritSectEnter(&(a_pShaper)->Lock); AssertRCReturn(rcShaper, rcShaper); } while (0)
74
75/** Releases the shaper lock (asserts on failure). */
76#define UNLOCK_NETSHAPER(a_pShaper) do { int rcShaper = RTCritSectLeave(&(a_pShaper)->Lock); AssertRC(rcShaper); } while (0)
77
78
79
80
81static PPDMNSBWGROUP pdmNsBwGroupFindById(PPDMNETSHAPER pShaper, const char *pszId)
82{
83 PPDMNSBWGROUP pBwGroup = NULL;
84
85 if (RT_VALID_PTR(pszId))
86 {
87 LOCK_NETSHAPER(pShaper);
88
89 pBwGroup = pShaper->pBwGroupsHead;
90 while ( pBwGroup
91 && RTStrCmp(pBwGroup->pszNameR3, pszId))
92 pBwGroup = pBwGroup->pNextR3;
93
94 UNLOCK_NETSHAPER(pShaper);
95 }
96
97 return pBwGroup;
98}
99
100
101static void pdmNsBwGroupLink(PPDMNSBWGROUP pBwGroup)
102{
103 PPDMNETSHAPER pShaper = pBwGroup->pShaperR3;
104 LOCK_NETSHAPER(pShaper);
105
106 pBwGroup->pNextR3 = pShaper->pBwGroupsHead;
107 pShaper->pBwGroupsHead = pBwGroup;
108
109 UNLOCK_NETSHAPER(pShaper);
110}
111
112
113#if 0
114static void pdmNsBwGroupUnlink(PPDMNSBWGROUP pBwGroup)
115{
116 PPDMNETSHAPER pShaper = pBwGroup->pShaper;
117 LOCK_NETSHAPER(pShaper);
118
119 if (pBwGroup == pShaper->pBwGroupsHead)
120 pShaper->pBwGroupsHead = pBwGroup->pNext;
121 else
122 {
123 PPDMNSBWGROUP pPrev = pShaper->pBwGroupsHead;
124 while ( pPrev
125 && pPrev->pNext != pBwGroup)
126 pPrev = pPrev->pNext;
127
128 AssertPtr(pPrev);
129 pPrev->pNext = pBwGroup->pNext;
130 }
131
132 UNLOCK_NETSHAPER(pShaper);
133}
134#endif
135
136
137static void pdmNsBwGroupSetLimit(PPDMNSBWGROUP pBwGroup, uint64_t cbPerSecMax)
138{
139 pBwGroup->cbPerSecMax = cbPerSecMax;
140 pBwGroup->cbBucket = RT_MAX(PDM_NETSHAPER_MIN_BUCKET_SIZE, cbPerSecMax * PDM_NETSHAPER_MAX_LATENCY / 1000);
141 LogFlow(("pdmNsBwGroupSetLimit: New rate limit is %llu bytes per second, adjusted bucket size to %u bytes\n",
142 pBwGroup->cbPerSecMax, pBwGroup->cbBucket));
143}
144
145
146static int pdmNsBwGroupCreate(PPDMNETSHAPER pShaper, const char *pszBwGroup, uint64_t cbPerSecMax)
147{
148 LogFlow(("pdmNsBwGroupCreate: pShaper=%#p pszBwGroup=%#p{%s} cbPerSecMax=%llu\n", pShaper, pszBwGroup, pszBwGroup, cbPerSecMax));
149
150 AssertPtrReturn(pShaper, VERR_INVALID_POINTER);
151 AssertPtrReturn(pszBwGroup, VERR_INVALID_POINTER);
152 AssertReturn(*pszBwGroup != '\0', VERR_INVALID_PARAMETER);
153
154 int rc;
155 PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pszBwGroup);
156 if (!pBwGroup)
157 {
158 rc = MMHyperAlloc(pShaper->pVM, sizeof(PDMNSBWGROUP), 64,
159 MM_TAG_PDM_NET_SHAPER, (void **)&pBwGroup);
160 if (RT_SUCCESS(rc))
161 {
162 rc = PDMR3CritSectInit(pShaper->pVM, &pBwGroup->Lock, RT_SRC_POS, "BWGRP");
163 if (RT_SUCCESS(rc))
164 {
165 pBwGroup->pszNameR3 = MMR3HeapStrDup(pShaper->pVM, MM_TAG_PDM_NET_SHAPER, pszBwGroup);
166 if (pBwGroup->pszNameR3)
167 {
168 pBwGroup->pShaperR3 = pShaper;
169 pBwGroup->cRefs = 0;
170
171 pdmNsBwGroupSetLimit(pBwGroup, cbPerSecMax);
172
173 pBwGroup->cbTokensLast = pBwGroup->cbBucket;
174 pBwGroup->tsUpdatedLast = RTTimeSystemNanoTS();
175
176 LogFlowFunc(("pszBwGroup={%s} cbBucket=%u\n",
177 pszBwGroup, pBwGroup->cbBucket));
178 pdmNsBwGroupLink(pBwGroup);
179 return VINF_SUCCESS;
180 }
181 PDMR3CritSectDelete(&pBwGroup->Lock);
182 }
183 MMHyperFree(pShaper->pVM, pBwGroup);
184 }
185 else
186 rc = VERR_NO_MEMORY;
187 }
188 else
189 rc = VERR_ALREADY_EXISTS;
190
191 LogFlowFunc(("returns rc=%Rrc\n", rc));
192 return rc;
193}
194
195
196static void pdmNsBwGroupTerminate(PPDMNSBWGROUP pBwGroup)
197{
198 Assert(pBwGroup->cRefs == 0);
199 if (PDMCritSectIsInitialized(&pBwGroup->Lock))
200 PDMR3CritSectDelete(&pBwGroup->Lock);
201}
202
203
204DECLINLINE(void) pdmNsBwGroupRef(PPDMNSBWGROUP pBwGroup)
205{
206 ASMAtomicIncU32(&pBwGroup->cRefs);
207}
208
209
210DECLINLINE(void) pdmNsBwGroupUnref(PPDMNSBWGROUP pBwGroup)
211{
212 Assert(pBwGroup->cRefs > 0);
213 ASMAtomicDecU32(&pBwGroup->cRefs);
214}
215
216
217static void pdmNsBwGroupXmitPending(PPDMNSBWGROUP pBwGroup)
218{
219 /*
220 * We don't need to hold the bandwidth group lock to iterate over the list
221 * of filters since the filters are removed while the shaper lock is being
222 * held.
223 */
224 AssertPtr(pBwGroup);
225 AssertPtr(pBwGroup->pShaperR3);
226 Assert(RTCritSectIsOwner(&pBwGroup->pShaperR3->Lock));
227 //LOCK_NETSHAPER(pShaper);
228
229 /* Check if the group is disabled. */
230 if (pBwGroup->cbPerSecMax == 0)
231 return;
232
233 PPDMNSFILTER pFilter = pBwGroup->pFiltersHeadR3;
234 while (pFilter)
235 {
236 bool fChoked = ASMAtomicXchgBool(&pFilter->fChoked, false);
237 Log3((LOG_FN_FMT ": pFilter=%#p fChoked=%RTbool\n", __PRETTY_FUNCTION__, pFilter, fChoked));
238 if (fChoked && pFilter->pIDrvNetR3)
239 {
240 LogFlowFunc(("Calling pfnXmitPending for pFilter=%#p\n", pFilter));
241 pFilter->pIDrvNetR3->pfnXmitPending(pFilter->pIDrvNetR3);
242 }
243
244 pFilter = pFilter->pNextR3;
245 }
246
247 //UNLOCK_NETSHAPER(pShaper);
248}
249
250
251static void pdmNsFilterLink(PPDMNSFILTER pFilter)
252{
253 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
254 int rc = PDMCritSectEnter(&pBwGroup->Lock, VERR_SEM_BUSY); AssertRC(rc);
255
256 pFilter->pNextR3 = pBwGroup->pFiltersHeadR3;
257 pBwGroup->pFiltersHeadR3 = pFilter;
258
259 rc = PDMCritSectLeave(&pBwGroup->Lock); AssertRC(rc);
260}
261
262
263static void pdmNsFilterUnlink(PPDMNSFILTER pFilter)
264{
265 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
266 /*
267 * We need to make sure we hold the shaper lock since pdmNsBwGroupXmitPending()
268 * does not hold the bandwidth group lock while iterating over the list
269 * of group's filters.
270 */
271 AssertPtr(pBwGroup);
272 AssertPtr(pBwGroup->pShaperR3);
273 Assert(RTCritSectIsOwner(&pBwGroup->pShaperR3->Lock));
274 int rc = PDMCritSectEnter(&pBwGroup->Lock, VERR_SEM_BUSY); AssertRC(rc);
275
276 if (pFilter == pBwGroup->pFiltersHeadR3)
277 pBwGroup->pFiltersHeadR3 = pFilter->pNextR3;
278 else
279 {
280 PPDMNSFILTER pPrev = pBwGroup->pFiltersHeadR3;
281 while ( pPrev
282 && pPrev->pNextR3 != pFilter)
283 pPrev = pPrev->pNextR3;
284
285 AssertPtr(pPrev);
286 pPrev->pNextR3 = pFilter->pNextR3;
287 }
288
289 rc = PDMCritSectLeave(&pBwGroup->Lock); AssertRC(rc);
290}
291
292
293/**
294 * Attach network filter driver from bandwidth group.
295 *
296 * @returns VBox status code.
297 * @param pVM Handle of VM.
298 * @param pDrvIns The driver instance.
299 * @param pszBwGroup Name of the bandwidth group to attach to.
300 * @param pFilter Pointer to the filter we attach.
301 */
302VMMR3_INT_DECL(int) PDMR3NsAttach(PUVM pUVM, PPDMDRVINS pDrvIns, const char *pszBwGroup, PPDMNSFILTER pFilter)
303{
304 VM_ASSERT_EMT(pUVM->pVM);
305 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
306 AssertReturn(pFilter->pBwGroupR3 == NULL, VERR_ALREADY_EXISTS);
307
308 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
309 LOCK_NETSHAPER_RETURN(pShaper);
310
311 int rc = VINF_SUCCESS;
312 PPDMNSBWGROUP pBwGroupNew = NULL;
313 if (pszBwGroup)
314 {
315 pBwGroupNew = pdmNsBwGroupFindById(pShaper, pszBwGroup);
316 if (pBwGroupNew)
317 pdmNsBwGroupRef(pBwGroupNew);
318 else
319 rc = VERR_NOT_FOUND;
320 }
321
322 if (RT_SUCCESS(rc))
323 {
324 PPDMNSBWGROUP pBwGroupOld = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, pBwGroupNew, PPDMNSBWGROUP);
325 ASMAtomicWritePtr(&pFilter->pBwGroupR0, MMHyperR3ToR0(pUVM->pVM, pBwGroupNew));
326 if (pBwGroupOld)
327 pdmNsBwGroupUnref(pBwGroupOld);
328 pdmNsFilterLink(pFilter);
329 }
330
331 UNLOCK_NETSHAPER(pShaper);
332 return rc;
333}
334
335
336/**
337 * Detach network filter driver from bandwidth group.
338 *
339 * @returns VBox status code.
340 * @param pUVM The user mode VM handle.
341 * @param pDrvIns The driver instance.
342 * @param pFilter Pointer to the filter we detach.
343 */
344VMMR3_INT_DECL(int) PDMR3NsDetach(PUVM pUVM, PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
345{
346 VM_ASSERT_EMT(pUVM->pVM);
347 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
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 Pointer to the VM.
410 * @param pThread The PDM thread data.
411 */
412static DECLCALLBACK(int) pdmR3NsTxThread(PVM pVM, PPDMTHREAD pThread)
413{
414 PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
415 LogFlow(("pdmR3NsTxThread: pShaper=%p\n", pShaper));
416 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
417 {
418 RTThreadSleep(PDM_NETSHAPER_MAX_LATENCY);
419
420 /* Go over all bandwidth groups/filters calling pfnXmitPending */
421 LOCK_NETSHAPER(pShaper);
422 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
423 while (pBwGroup)
424 {
425 pdmNsBwGroupXmitPending(pBwGroup);
426 pBwGroup = pBwGroup->pNextR3;
427 }
428 UNLOCK_NETSHAPER(pShaper);
429 }
430 return VINF_SUCCESS;
431}
432
433
434/**
435 * @copydoc FNPDMTHREADWAKEUPINT
436 */
437static DECLCALLBACK(int) pdmR3NsTxWakeUp(PVM pVM, PPDMTHREAD pThread)
438{
439 PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
440 LogFlow(("pdmR3NsTxWakeUp: pShaper=%p\n", pShaper));
441 /* Nothing to do */
442 return VINF_SUCCESS;
443}
444
445
446/**
447 * Terminate the network shaper.
448 *
449 * @returns VBox error code.
450 * @param pVM Pointer to VM.
451 *
452 * @remarks This method destroys all bandwidth group objects.
453 */
454int pdmR3NetShaperTerm(PVM pVM)
455{
456 PUVM pUVM = pVM->pUVM;
457 AssertPtrReturn(pUVM, VERR_INVALID_POINTER);
458 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
459 AssertPtrReturn(pShaper, VERR_INVALID_POINTER);
460
461 /* Destroy the bandwidth managers. */
462 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
463 while (pBwGroup)
464 {
465 PPDMNSBWGROUP pFree = pBwGroup;
466 pBwGroup = pBwGroup->pNextR3;
467 pdmNsBwGroupTerminate(pFree);
468 MMR3HeapFree(pFree->pszNameR3);
469 MMHyperFree(pVM, pFree);
470 }
471
472 RTCritSectDelete(&pShaper->Lock);
473 return VINF_SUCCESS;
474}
475
476
477/**
478 * Initialize the network shaper.
479 *
480 * @returns VBox status code
481 * @param pVM Pointer to the VM.
482 */
483int pdmR3NetShaperInit(PVM pVM)
484{
485 LogFlow(("pdmR3NetShaperInit: pVM=%p\n", pVM));
486 VM_ASSERT_EMT(pVM);
487 PUVM pUVM = pVM->pUVM;
488 AssertMsgReturn(!pUVM->pdm.s.pNetShaper, ("Network shaper was already initialized\n"), VERR_WRONG_ORDER);
489
490 PPDMNETSHAPER pShaper;
491 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_NET_SHAPER, sizeof(PDMNETSHAPER), (void **)&pShaper);
492 if (RT_SUCCESS(rc))
493 {
494 PCFGMNODE pCfgNetShaper = CFGMR3GetChild(CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM"), "NetworkShaper");
495
496 pShaper->pVM = pVM;
497 rc = RTCritSectInit(&pShaper->Lock);
498 if (RT_SUCCESS(rc))
499 {
500 /* Create all bandwidth groups. */
501 PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNetShaper, "BwGroups");
502 if (pCfgBwGrp)
503 {
504 for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur))
505 {
506 uint64_t cbMax;
507 size_t cbName = CFGMR3GetNameLen(pCur) + 1;
508 char *pszBwGrpId = (char *)RTMemAllocZ(cbName);
509
510 if (!pszBwGrpId)
511 {
512 rc = VERR_NO_MEMORY;
513 break;
514 }
515
516 rc = CFGMR3GetName(pCur, pszBwGrpId, cbName);
517 AssertRC(rc);
518
519 if (RT_SUCCESS(rc))
520 rc = CFGMR3QueryU64(pCur, "Max", &cbMax);
521 if (RT_SUCCESS(rc))
522 rc = pdmNsBwGroupCreate(pShaper, pszBwGrpId, cbMax);
523
524 RTMemFree(pszBwGrpId);
525
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.

© 2023 Oracle
ContactPrivacy policyTerms of Use