PDMNetShaper.cpp revision 4a268cd37879e04ad6ef4b8412bc2d293a44bc7a
13ce95e4c4dc7fb501c89c137a4ab8caecee6cbavboxsync * PDM Network Shaper - Limit network traffic according to bandwidth group settings.
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync * Copyright (C) 2011-2012 Oracle Corporation
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * available from http://www.virtualbox.org. This file is free software;
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * you can redistribute it and/or modify it under the terms of the GNU
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * General Public License (GPL) as published by the Free Software
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync/*******************************************************************************
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync* Header Files *
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync*******************************************************************************/
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync/*******************************************************************************
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync* Structures and Typedefs *
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync*******************************************************************************/
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * Network shaper data. One instance per VM.
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsynctypedef struct PDMNETSHAPER
c458503b85d643d51c5287959b8d96a3e32d9499vboxsync /** Pointer to the VM. */
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync /** Critical section protecting all members below. */
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync /** Pending TX thread. */
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync /** Pointer to the first bandwidth group. */
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsyncstatic PPDMNSBWGROUP pdmNsBwGroupFindById(PPDMNETSHAPER pShaper, const char *pcszId)
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsyncstatic void pdmNsBwGroupLink(PPDMNSBWGROUP pBwGroup)
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsyncstatic void pdmNsBwGroupUnlink(PPDMNSBWGROUP pBwGroup)
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
80bc18dbd439331cef10440eeca1cb36457b1d89vboxsyncstatic void pdmNsBwGroupSetLimit(PPDMNSBWGROUP pBwGroup, uint64_t cbTransferPerSecMax)
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync pBwGroup->cbTransferPerSecMax = cbTransferPerSecMax;
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync pBwGroup->cbBucketSize = RT_MAX(PDM_NETSHAPER_MIN_BUCKET_SIZE,
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync cbTransferPerSecMax * PDM_NETSHAPER_MAX_LATENCY / 1000);
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync LogFlowFunc(("New rate limit is %llu bytes per second, adjusted bucket size to %d bytes\n",
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync pBwGroup->cbTransferPerSecMax, pBwGroup->cbBucketSize));
80bc18dbd439331cef10440eeca1cb36457b1d89vboxsyncstatic int pdmNsBwGroupCreate(PPDMNETSHAPER pShaper, const char *pcszBwGroup, uint64_t cbTransferPerSecMax)
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync LogFlowFunc(("pShaper=%#p pcszBwGroup=%#p{%s} cbTransferPerSecMax=%llu\n",
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync pShaper, pcszBwGroup, pcszBwGroup, cbTransferPerSecMax));
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync AssertPtrReturn(pcszBwGroup, VERR_INVALID_POINTER);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync AssertReturn(*pcszBwGroup != '\0', VERR_INVALID_PARAMETER);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync rc = MMHyperAlloc(pShaper->pVM, sizeof(PDMNSBWGROUP), 64,
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync rc = PDMR3CritSectInit(pShaper->pVM, &pBwGroup->cs, RT_SRC_POS, "BWGRP");
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync pdmNsBwGroupSetLimit(pBwGroup, cbTransferPerSecMax);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsyncstatic void pdmNsBwGroupTerminate(PPDMNSBWGROUP pBwGroup)
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsyncDECLINLINE(void) pdmNsBwGroupRef(PPDMNSBWGROUP pBwGroup)
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsyncDECLINLINE(void) pdmNsBwGroupUnref(PPDMNSBWGROUP pBwGroup)
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsyncstatic void pdmNsBwGroupXmitPending(PPDMNSBWGROUP pBwGroup)
404dd7b4bf7209363c3ab5b98944ff8405e0130bvboxsync * We don't need to hold the bandwidth group lock to iterate over the list
404dd7b4bf7209363c3ab5b98944ff8405e0130bvboxsync * of filters since the filters are removed while the shaper lock is being
404dd7b4bf7209363c3ab5b98944ff8405e0130bvboxsync //int rc = RTCritSectEnter(&pBwGroup->cs); AssertRC(rc);
10b09c6100b7c788267d108dbb1c68dde55b053avboxsync /* Check if the group is disabled. */
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync bool fChoked = ASMAtomicXchgBool(&pFilter->fChoked, false);
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync Log3((LOG_FN_FMT ": pFilter=%#p fChoked=%RTbool\n", __PRETTY_FUNCTION__, pFilter, fChoked));
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync LogFlowFunc(("Calling pfnXmitPending for pFilter=%#p\n", pFilter));
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync pFilter->pIDrvNet->pfnXmitPending(pFilter->pIDrvNet);
404dd7b4bf7209363c3ab5b98944ff8405e0130bvboxsync //rc = RTCritSectLeave(&pBwGroup->cs); AssertRC(rc);
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync int rc = PDMCritSectEnter(&pBwGroup->cs, VERR_SEM_BUSY); AssertRC(rc);
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync rc = PDMCritSectLeave(&pBwGroup->cs); AssertRC(rc);
404dd7b4bf7209363c3ab5b98944ff8405e0130bvboxsync * We need to make sure we hold the shaper lock since pdmNsBwGroupXmitPending()
404dd7b4bf7209363c3ab5b98944ff8405e0130bvboxsync * does not hold the bandwidth group lock while iterating over the list
404dd7b4bf7209363c3ab5b98944ff8405e0130bvboxsync * of group's filters.
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync int rc = PDMCritSectEnter(&pBwGroup->cs, VERR_SEM_BUSY); AssertRC(rc);
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync rc = PDMCritSectLeave(&pBwGroup->cs); AssertRC(rc);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsyncVMMR3DECL(int) PDMR3NsAttach(PVM pVM, PPDMDRVINS pDrvIns, const char *pcszBwGroup,
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync AssertReturn(pFilter->pBwGroupR3 == NULL, VERR_ALREADY_EXISTS);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync pBwGroupNew = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync pBwGroupOld = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, pBwGroupNew, PPDMNSBWGROUP);
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync ASMAtomicWritePtr(&pFilter->pBwGroupR0, MMHyperR3ToR0(pVM, pBwGroupNew));
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync int rc2 = RTCritSectLeave(&pShaper->cs); AssertRC(rc2);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsyncVMMR3DECL(int) PDMR3NsDetach(PVM pVM, PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync AssertPtrReturn(pFilter->pBwGroupR3, VERR_INVALID_POINTER);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync pBwGroup = ASMAtomicXchgPtrT(&pFilter->pBwGroupR3, NULL, PPDMNSBWGROUP);
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync int rc2 = RTCritSectLeave(&pShaper->cs); AssertRC(rc2);
8980aa28561182fb74d04933f92587f7e28e9ca8vboxsyncVMMR3DECL(bool) PDMR3NsAllocateBandwidth(PPDMNSFILTER pFilter, size_t cbTransfer)
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync return pdmNsAllocateBandwidth(pFilter, cbTransfer);
80bc18dbd439331cef10440eeca1cb36457b1d89vboxsyncVMMR3DECL(int) PDMR3NsBwGroupSetLimit(PVM pVM, const char *pcszBwGroup, uint64_t cbTransferPerSecMax)
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync PPDMNSBWGROUP pBwGroup = pdmNsBwGroupFindById(pShaper, pcszBwGroup);
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync rc = PDMCritSectEnter(&pBwGroup->cs, VERR_SEM_BUSY); AssertRC(rc);
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync pdmNsBwGroupSetLimit(pBwGroup, cbTransferPerSecMax);
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync /* Drop extra tokens */
0247b3970a16ddb81e15bfd8ff8a4eb9747595ccvboxsync if (pBwGroup->cbTokensLast > pBwGroup->cbBucketSize)
1e80130a8de15c1a537ab0ffcf8999e986217a1avboxsync rc = PDMCritSectLeave(&pBwGroup->cs); AssertRC(rc);
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync * I/O thread for pending TX.
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync * @returns VINF_SUCCESS (ignored).
4bfa7b58e362a1bca0628643c352c137900bf01avboxsync * @param pVM Pointer to the VM.
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync * @param pThread The PDM thread data.
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsyncstatic DECLCALLBACK(int) pdmR3NsTxThread(PVM pVM, PPDMTHREAD pThread)
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync LogFlow(("pdmR3NsTxThread: pShaper=%p\n", pShaper));
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync /* Go over all bandwidth groups/filters calling pfnXmitPending */
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync int rc = RTCritSectEnter(&pShaper->cs); AssertRC(rc);
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync * @copydoc FNPDMTHREADWAKEUPINT
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsyncstatic DECLCALLBACK(int) pdmR3NsTxWakeUp(PVM pVM, PPDMTHREAD pThread)
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync PPDMNETSHAPER pShaper = (PPDMNETSHAPER)pThread->pvUser;
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync LogFlow(("pdmR3NsTxWakeUp: pShaper=%p\n", pShaper));
1e8d9e04805e6cf8e90e57ee08344105c76f23fevboxsync /* Nothing to do */
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * Terminate the network shaper.
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * @returns VBox error code.
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * @param pVM Pointer to VM.
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * @remarks This method destroys all bandwidth group objects.
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync /* Destroy the bandwidth managers. */
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * Initialize the network shaper.
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync * @returns VBox status code
c458503b85d643d51c5287959b8d96a3e32d9499vboxsync * @param pVM Pointer to the VM.
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync int rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_NET_SHAPER,
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync PCFGMNODE pCfgNetShaper = CFGMR3GetChild(CFGMR3GetChild(pCfgRoot, "PDM"), "NetworkShaper");
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync /* Create all bandwidth groups. */
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync PCFGMNODE pCfgBwGrp = CFGMR3GetChild(pCfgNetShaper, "BwGroups");
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync for (PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgBwGrp); pCur; pCur = CFGMR3GetNextChild(pCur))
b0af0b78d25ede09c0d23b2be9163cf43c7ca6f8vboxsync rc = pdmNsBwGroupCreate(pNetShaper, pszBwGrpId, cbMax);
13ce95e4c4dc7fb501c89c137a4ab8caecee6cbavboxsync AssertMsg(!pUVM->pdm.s.pNetShaper, ("Network shaper was already initialized\n"));
13ce95e4c4dc7fb501c89c137a4ab8caecee6cbavboxsync static unsigned s_iThread;
13ce95e4c4dc7fb501c89c137a4ab8caecee6cbavboxsync RTStrPrintf(szDesc, sizeof(szDesc), "PDMNsTx-%d", ++s_iThread);