VirtualBox

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

Last change on this file since 40652 was 40652, checked in by vboxsync, 13 years ago

NetShaper,E1000: Basic framework and partial implementation for network shaper

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: PDMNetShaper.cpp 40652 2012-03-26 16:36:16Z 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 shared VM structure. */
86 PVM pVM;
87 /** Critical section protecting all members below. */
88 RTCRITSECT cs;
89 /** Pointer to the first bandwidth group. */
90 PPDMNSBWGROUP pBwGroupsHead;
91} PDMNETSHAPER;
92
93
94
95/*******************************************************************************
96* Internal Functions *
97*******************************************************************************/
98
99static PPDMNSBWGROUP pdmNsBwGroupFindById(PPDMNETSHAPER pShaper, const char *pcszId)
100{
101 PPDMNSBWGROUP pBwGroup = NULL;
102
103 if (RT_VALID_PTR(pcszId))
104 {
105 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
106
107 pBwGroup = pShaper->pBwGroupsHead;
108 while ( pBwGroup
109 && RTStrCmp(pBwGroup->pszName, pcszId))
110 pBwGroup = pBwGroup->pNext;
111
112 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
113 }
114
115 return pBwGroup;
116}
117
118static void pdmNsBwGroupLink(PPDMNSBWGROUP pBwGroup)
119{
120 PPDMNETSHAPER pShaper = pBwGroup->pShaper;
121 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
122
123 pBwGroup->pNext = pShaper->pBwGroupsHead;
124 pShaper->pBwGroupsHead = pBwGroup;
125
126 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
127}
128
129#if 0
130static void pdmNsBwGroupUnlink(PPDMNSBWGROUP pBwGroup)
131{
132 PPDMNETSHAPER pShaper = pBwGroup->pShaper;
133 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
134
135 if (pBwGroup == pShaper->pBwGroupsHead)
136 pShaper->pBwGroupsHead = pBwGroup->pNext;
137 else
138 {
139 PPDMNSBWGROUP pPrev = pShaper->pBwGroupsHead;
140 while ( pPrev
141 && pPrev->pNext != pBwGroup)
142 pPrev = pPrev->pNext;
143
144 AssertPtr(pPrev);
145 pPrev->pNext = pBwGroup->pNext;
146 }
147
148 rc = RTCritSectLeave(&pShaper->cs); AssertRC(rc);
149}
150#endif
151
152static int pdmNsBwGroupCreate(PPDMNETSHAPER pShaper, const char *pcszBwGroup, uint32_t cbTransferPerSecMax)
153{
154 LogFlowFunc(("pShaper=%#p pcszBwGroup=%#p{%s} cbTransferPerSecMax=%u\n",
155 pShaper, pcszBwGroup, pcszBwGroup, cbTransferPerSecMax));
156
157 AssertPtrReturn(pShaper, VERR_INVALID_POINTER);
158 AssertPtrReturn(pcszBwGroup, VERR_INVALID_POINTER);
159 AssertReturn(*pcszBwGroup != '\0', VERR_INVALID_PARAMETER);
160
161 int rc;
162 PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
163 if (!pBwGroup)
164 {
165 rc = MMR3HeapAllocZEx(pShaper->pVM, MM_TAG_PDM_NET_SHAPER,
166 sizeof(PDMNSBWGROUP),
167 (void **)&pBwGroup);
168 if (RT_SUCCESS(rc))
169 {
170 rc = RTCritSectInit(&pBwGroup->cs);
171 if (RT_SUCCESS(rc))
172 {
173 pBwGroup->pszName = RTStrDup(pcszBwGroup);
174 if (pBwGroup->pszName)
175 {
176 pBwGroup->pShaper = pShaper;
177 pBwGroup->cRefs = 0;
178
179 pBwGroup->cbTransferPerSecMax = cbTransferPerSecMax;
180 pBwGroup->cbBucketSize = RT_MAX(PDM_NETSHAPER_MIN_BUCKET_SIZE,
181 cbTransferPerSecMax * PDM_NETSHAPER_MAX_LATENCY / 1000);
182 pBwGroup->cbTokensLast = pBwGroup->cbBucketSize;
183 pBwGroup->tsUpdatedLast = RTTimeSystemNanoTS();
184
185 LogFlowFunc(("pcszBwGroup={%s} cbBucketSize=%u\n",
186 pcszBwGroup, pBwGroup->cbBucketSize));
187 pdmNsBwGroupLink(pBwGroup);
188 return VINF_SUCCESS;
189 }
190 RTCritSectDelete(&pBwGroup->cs);
191 }
192 MMR3HeapFree(pBwGroup);
193 }
194 else
195 rc = VERR_NO_MEMORY;
196 }
197 else
198 rc = VERR_ALREADY_EXISTS;
199
200 LogFlowFunc(("returns rc=%Rrc\n", rc));
201 return rc;
202}
203
204static void pdmNsBwGroupTerminate(PPDMNSBWGROUP pBwGroup)
205{
206 Assert(pBwGroup->cRefs == 0);
207 if (RTCritSectIsInitialized(&pBwGroup->cs))
208 RTCritSectDelete(&pBwGroup->cs);
209}
210
211
212DECLINLINE(void) pdmNsBwGroupRef(PPDMNSBWGROUP pBwGroup)
213{
214 ASMAtomicIncU32(&pBwGroup->cRefs);
215}
216
217DECLINLINE(void) pdmNsBwGroupUnref(PPDMNSBWGROUP pBwGroup)
218{
219 Assert(pBwGroup->cRefs > 0);
220 ASMAtomicDecU32(&pBwGroup->cRefs);
221}
222
223static void pdmNsFilterLink(PPDMNSFILTER pFilter)
224{
225 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
226 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
227
228 pFilter->pNext = pBwGroup->pFiltersHead;
229 pBwGroup->pFiltersHead = pFilter;
230
231 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
232}
233
234static void pdmNsFilterUnlink(PPDMNSFILTER pFilter)
235{
236 PPDMNSBWGROUP pBwGroup = pFilter->pBwGroupR3;
237 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
238
239 if (pFilter == pBwGroup->pFiltersHead)
240 pBwGroup->pFiltersHead = pFilter->pNext;
241 else
242 {
243 PPDMNSFILTER pPrev = pBwGroup->pFiltersHead;
244 while ( pPrev
245 && pPrev->pNext != pFilter)
246 pPrev = pPrev->pNext;
247
248 AssertPtr(pPrev);
249 pPrev->pNext = pFilter->pNext;
250 }
251
252 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
253}
254
255VMMR3DECL(int) PDMR3NsAttach(PVM pVM, PPDMDRVINS pDrvIns, const char *pcszBwGroup,
256 PPDMNSFILTER pFilter)
257{
258 VM_ASSERT_EMT(pVM);
259 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
260 AssertReturn(pFilter->pBwGroupR3 == NULL, VERR_ALREADY_EXISTS);
261
262
263 PUVM pUVM = pVM->pUVM;
264 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
265
266 PPDMNSBWGROUP pBwGroupOld = NULL;
267 PPDMNSBWGROUP pBwGroupNew = NULL;
268
269 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
270 if (RT_SUCCESS(rc))
271 {
272 if (pcszBwGroup)
273 {
274 pBwGroupNew = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
275 if (pBwGroupNew)
276 pdmNsBwGroupRef(pBwGroupNew);
277 else
278 rc = VERR_NOT_FOUND;
279 }
280
281 if (RT_SUCCESS(rc))
282 {
283 pBwGroupOld = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, pBwGroupNew, PPDMNSBWGROUP);
284 if (pBwGroupOld)
285 pdmNsBwGroupUnref(pBwGroupOld);
286 pdmNsFilterLink(pFilter);
287 }
288 int rc2 = RTCritSectLeave(&pShaper->cs); AssertRC(rc2);
289 }
290
291 return rc;
292}
293
294VMMR3DECL(int) PDMR3NsDetach(PVM pVM, PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
295{
296 VM_ASSERT_EMT(pVM);
297 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
298 AssertPtrReturn(pFilter->pBwGroupR3, VERR_INVALID_POINTER);
299
300 PUVM pUVM = pVM->pUVM;
301 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
302
303 int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
304 if (RT_SUCCESS(rc))
305 {
306 pdmNsFilterUnlink(pFilter);
307 PPDMNSBWGROUP pBwGroup = NULL;
308 pBwGroup = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, NULL, PPDMNSBWGROUP);
309 if (pBwGroup)
310 pdmNsBwGroupUnref(pBwGroup);
311 int rc2 = RTCritSectLeave(&pShaper->cs); AssertRC(rc2);
312 }
313 return rc;
314}
315
316bool PDMR3NsAllocateBandwidth(PPDMNSFILTER pFilter, uint32_t cbTransfer)
317{
318 if (!VALID_PTR(pFilter->pBwGroupR3))
319 return true;
320
321 PPDMNSBWGROUP pBwGroup = ASMAtomicReadPtrT(&pFilter->pBwGroupR3, PPDMNSBWGROUP);
322 int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
323 bool fAllowed = true;
324 /* Re-fill the bucket first */
325 uint64_t tsNow = RTTimeSystemNanoTS();
326 uint32_t uTokensAdded = (tsNow - pBwGroup->tsUpdatedLast)*pBwGroup->cbTransferPerSecMax/(1000*1000*1000);
327 uint32_t uTokens = RT_MIN(pBwGroup->cbBucketSize, uTokensAdded + pBwGroup->cbTokensLast);
328
329 if (cbTransfer > uTokens)
330 fAllowed = false;
331 else
332 {
333 pBwGroup->tsUpdatedLast = tsNow;
334 pBwGroup->cbTokensLast = uTokens - cbTransfer;
335 }
336
337 rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
338 LogFlowFunc(("BwGroup=%#p{%s} cbTransfer=%u uTokens=%u uTokensAdded=%u fAllowed=%RTbool\n",
339 pBwGroup, pBwGroup->pszName, cbTransfer, uTokens, uTokensAdded, fAllowed));
340 return true; // @todo: i need to implement TX thread first! return fAllowed;
341}
342
343
344/**
345 * Terminate the network shaper.
346 *
347 * @returns VBox error code.
348 * @param pVM Pointer to VM.
349 *
350 * @remarks This method destroys all bandwidth group objects.
351 */
352int pdmR3NetShaperTerm(PVM pVM)
353{
354 PUVM pUVM = pVM->pUVM;
355 PPDMNETSHAPER pShaper = pUVM->pdm.s.pNetShaper;
356
357 /* Destroy the bandwidth managers. */
358 PPDMNSBWGROUP pBwGroup = pShaper->pBwGroupsHead;
359 while (pBwGroup)
360 {
361 PPDMNSBWGROUP pFree = pBwGroup;
362 pBwGroup = pBwGroup->pNext;
363 pdmNsBwGroupTerminate(pFree);
364 MMR3HeapFree(pFree);
365 }
366
367 RTCritSectDelete(&pShaper->cs);
368 return VINF_SUCCESS;
369}
370
371/**
372 * Initialize the network shaper.
373 *
374 * @returns VBox status code
375 * @param pVM Pointer to the shared VM structure.
376 */
377int pdmR3NetShaperInit(PVM pVM)
378{
379 LogFlowFunc((": pVM=%p\n", pVM));
380
381 VM_ASSERT_EMT(pVM);
382
383 PPDMNETSHAPER pNetShaper = NULL;
384
385 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_NET_SHAPER,
386 sizeof(PDMNETSHAPER),
387 (void **)&pNetShaper);
388 if (RT_SUCCESS(rc))
389 {
390 PCFGMNODE pCfgRoot = CFGMR3GetRoot(pVM);
391 PCFGMNODE pCfgNetShaper = CFGMR3GetChild(CFGMR3GetChild(pCfgRoot, "PDM"), "NetworkShaper");
392
393 pNetShaper->pVM = pVM;
394 rc = RTCritSectInit(&pNetShaper->cs);
395 if (RT_SUCCESS(rc))
396 {
397 /* Create all bandwidth groups. */
398 PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNetShaper, "BwGroups");
399
400 if (pCfgBwGrp)
401 {
402 for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur))
403 {
404 uint32_t cbMax;
405 size_t cchName = CFGMR3GetNameLen(pCur) + 1;
406 char *pszBwGrpId = (char *)RTMemAllocZ(cchName);
407
408 if (!pszBwGrpId)
409 {
410 rc = VERR_NO_MEMORY;
411 break;
412 }
413
414 rc = CFGMR3GetName(pCur, pszBwGrpId, cchName);
415 AssertRC(rc);
416
417 if (RT_SUCCESS(rc))
418 rc = CFGMR3QueryU32(pCur, "Max", &cbMax);
419 if (RT_SUCCESS(rc))
420 rc = pdmNsBwGroupCreate(pNetShaper, pszBwGrpId, cbMax);
421
422 RTMemFree(pszBwGrpId);
423
424 if (RT_FAILURE(rc))
425 break;
426 }
427 }
428
429 if (RT_SUCCESS(rc))
430 {
431 PUVM pUVM = pVM->pUVM;
432 AssertMsg(!pUVM->pdm.s.pNetShaper,
433 ("Network shaper was already initialized\n"));
434
435 pUVM->pdm.s.pNetShaper = pNetShaper;
436 return VINF_SUCCESS;
437 }
438
439 RTCritSectDelete(&pNetShaper->cs);
440 }
441 MMR3HeapFree(pNetShaper);
442 }
443
444 LogFlowFunc((": pVM=%p rc=%Rrc\n", pVM, rc));
445 return rc;
446}
447
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