/* - QOS.CPP - * Microsoft NetMeeting * Quality of Service DLL * IQoS interfaces * * Revision History: * * When Who What * -------- ------------------ --------------------------------------- * 10.23.96 Yoram Yaacovi Created * * Functions: * IQoS * CQoS::QueryInterface * CQoS::AddRef * CQoS::Release * CQoS::RequestResources * CQoS::ReleaseResources * CQoS::SetClients * CQoS::SetResources * CQoS::GetResources * CQoS::FreeBuffer * Public: * CQoS::CQoS * CQoS::~CQoS * CQoS::Initialize * Private * CQoS::AnyRequests * CQoS::FindClientsForResource * CQoS::StoreResourceRequest * CQoS::FreeResourceRequest * CQoS::UpdateClientInfo * CQoS::QoSCleanup * CQoS::FindClient * CQoS::UpdateRequestsForClient * External * CreateQoS * QoSEntryPoint */ #include "precomp.h" EXTERN_C int g_cQoSObjects=0; EXTERN_C HANDLE g_hQoSMutex=NULL; class CQoS *g_pQoS; #ifdef DEBUG HDBGZONE ghDbgZoneQoS = NULL; static PTCHAR _rgZonesQos[] = { TEXT("qos"), TEXT("Init"), TEXT("IQoS"), TEXT("Thread"), TEXT("Structures"), TEXT("Parameters"), }; #endif /* DEBUG */ /*************************************************************************** Name : QoSCleanup Purpose : Cleans up a QoS object before releasing (free memory, etc) Parameters: pqos - pointer to a QoS pbject Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::QoSCleanup () { HRESULT hr=NOERROR; LPRESOURCEINT pr=NULL; LPCLIENTINT pc=NULL; ACQMUTEX(g_hQoSMutex); /* * Free memory */ // traverse and free all the memory allocated by the QoS object // resources and requests first pr = m_pResourceList; while (pr) { LPRESOURCEINT prNext=pr->fLink; // first, delete the request list for this resource FreeListOfRequests(&(pr->pRequestList)); MEMFREE(pr); pr = prNext; } m_pResourceList = 0; // next is clients pc = m_pClientList; while (pc) { LPCLIENTINT pcNext=pc->fLink; // delete the request list for this client FreeListOfRequests(&(pc->pRequestList)); // now delete the client itself MEMFREE(pc); pc = pcNext; } m_pClientList = 0; // terminate the QoS thread and let it exit // the thread should really be terminated when the last request // is released, so this is just a safety measure StopQoSThread(); // delete the events if (m_evImmediateNotify) CloseHandle(m_evImmediateNotify); m_evImmediateNotify = NULL; if (m_evThreadExitSignal) CloseHandle(m_evThreadExitSignal); m_evThreadExitSignal = NULL; RELMUTEX(g_hQoSMutex); return hr; } /*************************************************************************** Name : AnyRequests Purpose : Finds out if there are any resource requests Parameters: none Returns : TRUE - there is at least one request Comment : ***************************************************************************/ BOOL CQoS::AnyRequests(void) { LPRESOURCEINT pr=NULL; BOOL bAnyRequests=FALSE; pr = m_pResourceList; while (pr) { if (pr->pRequestList) { bAnyRequests=TRUE; break; } // next resource pr = pr->fLink; } return bAnyRequests; } /*************************************************************************** Name : FindClientsForResource Purpose : Finds if there are clients for a specific resource Parameters: [in] dwResourceID = the ID of the resource [in] pc = client pointer to start searching from [out] puSamePriClients = number of clients with the same priority for this resource is returned here [out] puLowerPriClients = number of clients with lower priority for this resource is returned here Returns : HRESULT Comment : This function is NOT general purpose. It only counts clients with the same priority DOWN the list. ***************************************************************************/ HRESULT CQoS::FindClientsForResource( DWORD dwResourceID, LPCLIENTINT pc, ULONG *puSamePriClients, ULONG *puLowerPriClients) { LPCLIENTINT pctemp=pc->fLink; LPRESOURCEREQUESTINT pcrr=NULL; *puLowerPriClients = 0; *puSamePriClients = 1; // the first client (at 'pc') while (pctemp) { LPRESOURCEINT pr=NULL; // does this client need this specific resource ? pcrr = pctemp->pRequestList; while (pcrr) { if (pcrr->sResourceRequest.resourceID == dwResourceID) { // it is either a same priority client or a lower priority // client (the list is sorted) (pctemp->client.priority == pc->client.priority ? (*puSamePriClients)++ : (*puLowerPriClients)++); break; } // next request for this client pcrr = pcrr->fLink; } pctemp = pctemp->fLink; } return NOERROR; } /*************************************************************************** Name : FreeListOfRequests Purpose : Free all records of a linked list of requests, given the address of the list pointer. Zero's the list pointer Parameters: lppRequestList - address of the pointer to the beginning of the list Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::FreeListOfRequests(LPRESOURCEREQUESTINT *lppRequestList) { LPRESOURCEREQUESTINT prr=*lppRequestList; HRESULT hr=NOERROR; while (prr) { LPRESOURCEREQUESTINT prrNext=prr->fLink; MEMFREE(prr); prr = prrNext; } *lppRequestList = NULL; return hr; } /*************************************************************************** Name : FreeResourceRequest Purpose : Frees resource units and respective resource requests Parameters: pClientGUID - the GUID of the calling client (stream) pnUnits - a pointer of where to return the number of units freed pResourceInt - pointer to the resource being freed Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::FreeResourceRequest ( LPGUID pClientGUID, LPRESOURCEINT pResourceInt, int *pnUnits) { HRESULT hr=NOERROR; LPRESOURCEREQUESTINT prr=NULL, *prrPrev=NULL; // find the request from this client prr = pResourceInt->pRequestList; prrPrev = &(pResourceInt->pRequestList); while (prr) { if (COMPARE_GUIDS(&(prr->guidClientGUID), pClientGUID)) { // we do have a request from this client. // reclaim the units... *pnUnits = prr->sResourceRequest.nUnitsMin; // ...and remove it *prrPrev = prr->fLink; MEMFREE(prr); // we're done. hr = NOERROR; goto out; } prrPrev = &(prr->fLink); prr = prr->fLink; } hr = QOS_E_NO_SUCH_REQUEST; out: return hr; } /*************************************************************************** Name : StoreResourceRequest Purpose : Stores a resource request with the resource Parameters: pClientGUID - the GUID of the calling client (stream) pResourceRequest - the request to store pfnQoSNotify - a pointer to a notification function for the requesting client pResourceInt - pointer to the resource on which to store the request Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::StoreResourceRequest (LPGUID pClientGUID, LPRESOURCEREQUEST pResourceRequest, LPFNQOSNOTIFY pfnQoSNotify, DWORD_PTR dwParam, LPRESOURCEINT pResourceInt) { HRESULT hr=NOERROR; LPRESOURCEREQUESTINT prr=NULL, *prrPrev=NULL; BOOL fRequestFound=FALSE; /* * Store the request */ // do we already have a request from this client ? prr = pResourceInt->pRequestList; prrPrev = &(pResourceInt->pRequestList); while (prr) { if (COMPARE_GUIDS(&(prr->guidClientGUID), pClientGUID)) { // we do have a request from this client. override it. RtlCopyMemory( &(prr->sResourceRequest), pResourceRequest, sizeof(RESOURCEREQUEST)); RtlCopyMemory(&(prr->guidClientGUID), pClientGUID, sizeof(GUID)); prr->pfnQoSNotify = pfnQoSNotify; prr->dwParam = dwParam; // we're done. hr = NOERROR; fRequestFound = TRUE; break; } prrPrev = &(prr->fLink); prr = prr->fLink; } if (!fRequestFound) { // not found. make one prr = (LPRESOURCEREQUESTINT) MEMALLOC(sizeof(RESOURCEREQUESTINT)); ASSERT(prr); if (!prr) { ERRORMSG(("StoreResourceRequest: MEMALLOC failed on RESOURCEREQUESTINT\n")); hr = E_OUTOFMEMORY; goto out; } *prrPrev = prr; prr->fLink = NULL; } // whether found or made, copy the contents in RtlCopyMemory( &(prr->sResourceRequest), pResourceRequest, sizeof(RESOURCEREQUEST)); RtlCopyMemory(&(prr->guidClientGUID), pClientGUID, sizeof(GUID)); prr->pfnQoSNotify = pfnQoSNotify; prr->dwParam = dwParam; out: return hr; } /*************************************************************************** Name : UpdateClientInfo Purpose : Updates the client info when a resource request is granted Parameters: pClientGUID - the GUID of the calling client (stream) pfnQoSNotify - a pointer to a notification function for the requesting client Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::UpdateClientInfo (LPGUID pClientGUID, LPFNQOSNOTIFY pfnQoSNotify) { HRESULT hr=NOERROR; LPCLIENTLIST pcl=NULL; /* * Update client info */ // we'll do this through calling the SetClients method // allocate and fill a CLIENTLIST structure pcl = (LPCLIENTLIST) MEMALLOC(sizeof(CLIENTLIST)); if (!pcl) { ERRORMSG(("UpdateClientInfo: MEMALLOC failed\n")); hr = E_OUTOFMEMORY; goto out; } RtlZeroMemory((PVOID) pcl, sizeof(CLIENTLIST)); // fill in the resource list pcl->cClients = 1; RtlCopyMemory(&(pcl->aClients[0].guidClientGUID), pClientGUID, sizeof(GUID)); pcl->aClients[0].priority = QOS_LOWEST_PRIORITY; // set the clients info on the QoS module hr = SetClients(pcl); out: if (pcl) MEMFREE(pcl); return hr; } /*************************************************************************** Name : UpdateRequestsForClient Purpose : Update a client's request list by finding all existing resource requests for this client Parameters: pClientGUID - the GUID of the calling client (stream) Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::UpdateRequestsForClient (LPGUID pClientGUID) { HRESULT hr=NOERROR; LPRESOURCEINT pr=NULL; LPCLIENTINT pc=NULL; LPRESOURCEREQUESTINT prr=NULL, *pcrrfLink=NULL, pcrr=NULL; /* * get rid of the current request list for this client */ // find the client first hr = FindClient(pClientGUID, &pc); if (FAILED(hr) || !pc) { hr = QOS_E_NO_SUCH_CLIENT; goto out; } // now delete old request list FreeListOfRequests(&(pc->pRequestList)); /* * create and add the new request list */ pr = m_pResourceList; pcrrfLink = &(pc->pRequestList); while (pr) { prr = pr->pRequestList; while (prr) { if (COMPARE_GUIDS(&(prr->guidClientGUID), pClientGUID)) { // we found a request from this client. // allocate memory for it, and copy it in pcrr = (LPRESOURCEREQUESTINT) MEMALLOC(sizeof(RESOURCEREQUESTINT)); ASSERT(pcrr); if (!pcrr) { ERRORMSG(("UpdateRequestsForClient: MEMALLOC failed on RESOURCEREQUESTINT\n")); hr = E_OUTOFMEMORY; goto out; } // copy the contents in RtlCopyMemory(pcrr, prr, sizeof(RESOURCEREQUESTINT)); // need a different fLink for the client request list *pcrrfLink = pcrr; pcrr->fLink = NULL; pcrrfLink = &(pcrr->fLink); } // next request prr = prr->fLink; } // next resource pr = pr->fLink; } out: return hr; } /*************************************************************************** Name : FindClient Purpose : Finds and returns a client record Parameters: pClientGUID - the GUID whose record to find ppClient - address of where to put a pointer to the client found Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::FindClient(LPGUID pClientGUID, LPCLIENTINT *ppClient) { LPCLIENTINT pc=NULL; HRESULT hr=NOERROR; *ppClient = NULL; pc = m_pClientList; while (pc) { if (COMPARE_GUIDS(&(pc->client.guidClientGUID), pClientGUID)) { *ppClient = pc; goto out; } // next client pc = pc->fLink; } hr = QOS_E_NO_SUCH_CLIENT; out: return hr; } /*************************************************************************** IUnknown Methods ***************************************************************************/ HRESULT CQoS::QueryInterface (REFIID riid, LPVOID *lppNewObj) { HRESULT hr = NOERROR; DEBUGMSG(ZONE_IQOS,("IQoS::QueryInterface\n")); if (IsBadReadPtr(&riid, (UINT) sizeof(IID))) { hr = ResultFromScode(E_INVALIDARG); goto out; } if (IsBadWritePtr(lppNewObj, sizeof(LPVOID))) { hr = ResultFromScode(E_INVALIDARG); goto out; } *lppNewObj = 0; if (riid == IID_IUnknown || riid == IID_IQoS) *lppNewObj = (IQoS *) this; else { hr = E_NOINTERFACE; goto out; } ((IUnknown *)*lppNewObj)->AddRef (); out: DEBUGMSG(ZONE_IQOS,("IQoS::QueryInterface - leave, hr=0x%x\n", hr)); return hr; } ULONG CQoS::AddRef (void) { DEBUGMSG(ZONE_IQOS,("IQoS::AddRef\n")); InterlockedIncrement((long *) &m_cRef); DEBUGMSG(ZONE_IQOS,("IQoS::AddRef - leave, m_cRef=%d\n", m_cRef)); return m_cRef; } ULONG CQoS::Release (void) { DEBUGMSG(ZONE_IQOS,("IQoS::Release\n")); // if the cRef is already 0 (shouldn't happen), assert, but let it through ASSERT(m_cRef); if (InterlockedDecrement((long *) &m_cRef) == 0) { if (m_bQoSEnabled) QoSCleanup(); delete this; DEBUGMSG(ZONE_IQOS,("IQoS::Final Release\n")); return 0; } DEBUGMSG(ZONE_IQOS,("IQoS::Release - leave, m_cRef=%d\n", m_cRef)); return m_cRef; } /*************************************************************************** Name : CQoS::RequestResources Purpose : Requests resources Parameters: lpStreamGUID - the GUID of the calling client (stream) lpResourceRequestList - a list of resource requests that the caller wants to reserve lpfnQoSNotify - a pointer to a notification function for the requesting client Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::RequestResources (LPGUID lpClientGUID, LPRESOURCEREQUESTLIST lpResourceRequestList, LPFNQOSNOTIFY lpfnQoSNotify, DWORD_PTR dwParam) { HRESULT hr = NOERROR; ULONG i; BOOL fResourceFound=FALSE, fRequestGranted=FALSE; LPRESOURCEINT pResourceInt=NULL, *pPrevResourcefLink=NULL; RESOURCEREQUEST *pResourceRequest=NULL; DEBUGMSG(ZONE_IQOS,("IQoS::RequestResources\n")); /* * Parameter validation */ // lpResourceRequestList should at least have a count DWORD // must have a GUID and a notify callback if (!lpResourceRequestList || IsBadReadPtr(lpResourceRequestList, (UINT) sizeof(DWORD)) || !lpClientGUID || !lpfnQoSNotify) { hr = E_INVALIDARG; goto out_nomutex; } DISPLAYPARAMETERS( REQUEST_RESOURCES_ID, lpClientGUID, lpResourceRequestList, lpfnQoSNotify, dwParam, 0); ACQMUTEX(g_hQoSMutex); if (!m_bQoSEnabled) // just return goto out; /* * Find and allocate the resources */ // for each requested resource pResourceRequest=lpResourceRequestList->aRequests; for (i=0; i < lpResourceRequestList->cRequests; i++) { pResourceInt = m_pResourceList; fResourceFound = FALSE; // find the resource while (pResourceInt) { if (pResourceInt->resource.resourceID == pResourceRequest[i].resourceID) { // resource found // see if the resource is available // priority will be handled at the first notify callback // CHECK: add nUnitsMax handling if (pResourceRequest[i].nUnitsMin <= pResourceInt->nNowAvailUnits) { // resource is available. take the requested share. pResourceInt->nNowAvailUnits -= pResourceRequest[i].nUnitsMin; // store a local copy of the request pResourceRequest[i].hResult = StoreResourceRequest(lpClientGUID, &(pResourceRequest[i]), lpfnQoSNotify, dwParam, pResourceInt); // if we failed storing, propagate the result to the bottom line // returned result if (FAILED(pResourceRequest[i].hResult)) { hr = pResourceRequest[i].hResult; } else { // at least one request was granted to this client fRequestGranted = TRUE; } } else // resource not available { // let the client know how much is available pResourceRequest[i].nUnitsMin = pResourceInt->nNowAvailUnits; pResourceRequest[i].hResult = QOS_E_RES_NOT_ENOUGH_UNITS; hr = QOS_E_REQ_ERRORS; } fResourceFound = TRUE; break; } // not this one. try next one. pResourceInt = pResourceInt->fLink; } // while if (!fResourceFound) { pResourceRequest[i].hResult = QOS_E_RES_NOT_AVAILABLE; hr = QOS_E_REQ_ERRORS; } // next request } // for // if we allocated resources to this client, add it to the client list, // provided that it is not already in the list // special case: if the call to RequestResources was made from the QoS // notification proc, no need to update the client info. Actually, it will // be bad to do this, since we are traversing the client list in the // notify proc right at this moment... if (fRequestGranted && !m_bInNotify) { // add (or update) the client list with this client HRESULT hrTemp=NOERROR; LPCLIENTINT pc=NULL; // if the client is not already in the client list - add it FindClient(lpClientGUID, &pc); if (!pc) { hrTemp = UpdateClientInfo (lpClientGUID, lpfnQoSNotify); if (FAILED(hrTemp)) hr = hrTemp; } // also, make a note that RequestResources has been called. This will // make the QoS thread skip one heartbeat in order not call a client // too early m_nSkipHeartBeats = 1; // we have at least one request, so spawn the QoS thread, if not // already running if (!m_hThread) hrTemp = StartQoSThread(); } out: DISPLAYQOSOBJECT(); RELMUTEX(g_hQoSMutex); out_nomutex: DEBUGMSG(ZONE_IQOS,("IQoS::RequestResources - leave, hr=0x%x\n", hr)); return hr; } /*************************************************************************** Name : CQoS::ReleaseResources Purpose : Releases resources Parameters: lpClientGUID - the GUID of the calling client (stream) lpResourceRequestList - a list of resource requests that the caller wants to reserve Returns : HRESULT Comment : The values in the resource list are ignored. The resources specified are freed. ***************************************************************************/ HRESULT CQoS::ReleaseResources (LPGUID lpClientGUID, LPRESOURCEREQUESTLIST lpResourceRequestList) { ULONG i; int nUnits=0; BOOL fResourceFound=FALSE; LPRESOURCEINT pResourceInt=NULL, *pPrevResourcefLink=NULL; RESOURCEREQUEST *pResourceRequest=NULL; HRESULT hr = NOERROR; DEBUGMSG(ZONE_IQOS,("IQoS::ReleaseResources\n")); /* * parameter validation */ // lpResourceRequestList should at least have a count DWORD if (!lpResourceRequestList || IsBadReadPtr(lpResourceRequestList, (UINT) sizeof(DWORD))) { hr = E_INVALIDARG; goto out_nomutex; } DISPLAYPARAMETERS( RELEASE_RESOURCES_ID, lpClientGUID, lpResourceRequestList, 0, 0, 0); ACQMUTEX(g_hQoSMutex); if (!m_bQoSEnabled) // just return goto out; // for each requested resource pResourceRequest=lpResourceRequestList->aRequests; for (i=0; i < lpResourceRequestList->cRequests; i++) { // make sure we start with no error (caller might not cleared last hresult) pResourceRequest[i].hResult = NOERROR; pResourceInt = m_pResourceList; fResourceFound = FALSE; // find the resource while (pResourceInt) { if (pResourceInt->resource.resourceID == pResourceRequest[i].resourceID) { // resource found // free the local copy of the request pResourceRequest[i].hResult = FreeResourceRequest(lpClientGUID, pResourceInt, &nUnits); // if succeeded, claim the units back if (SUCCEEDED(pResourceRequest[i].hResult) && (nUnits >= 0)) { // add the freed units pResourceInt->nNowAvailUnits += nUnits; // in case something went wrong and we now have more available units // than total ones // NOTE: the ASSERT below is no longer proper. If SetResources was called, // and decreased the total units for a resource while there were // requests on this resource, the available units for this resource // might exceed the total one if released. Since QoS will auto-repair // this in the next notify cycle, the window for this is very small // ASSERT(!(pResourceInt->nNowAvailUnits > pResourceInt->resource.nUnits)); if (pResourceInt->nNowAvailUnits > pResourceInt->resource.nUnits) { // we don't want to have more available units than total pResourceInt->nNowAvailUnits = pResourceInt->resource.nUnits; } } else { // no such request pResourceRequest[i].hResult = QOS_E_NO_SUCH_REQUEST; hr = QOS_E_REQ_ERRORS; } fResourceFound = TRUE; break; } // not this one. try next one. pResourceInt = pResourceInt->fLink; } // while if (!fResourceFound) { pResourceRequest[i].hResult = QOS_E_NO_SUCH_RESOURCE; hr = QOS_E_REQ_ERRORS; } // next request } // if no requests left, can let the notification thread go... if (m_hThread && !AnyRequests()) { // stop the thread StopQoSThread(); } out: DISPLAYQOSOBJECT(); RELMUTEX(g_hQoSMutex); out_nomutex: DEBUGMSG(ZONE_IQOS,("IQoS::ReleaseResources - leave, hr=0x%x\n", hr)); return hr; } /*************************************************************************** Name : CQoS::SetResources Purpose : Sets the available resources on the QoS module Parameters: lpResourceList - list of resources and their availability Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::SetResources (LPRESOURCELIST lpResourceList) { HRESULT hr = NOERROR; ULONG i; BOOL fResourceFound=FALSE; LPRESOURCEINT pResourceInt=NULL, *pPrevResourcefLink=NULL; RESOURCE *pResource=NULL; RegEntry reQoSResourceRoot(REGKEY_QOS_RESOURCES, HKEY_LOCAL_MACHINE, FALSE, KEY_READ); DEBUGMSG(ZONE_IQOS,("IQoS::SetResources\n")); /* * parameter validation */ // lpResourceList should at least have a count DWORD if (!lpResourceList || IsBadReadPtr(lpResourceList, (UINT) sizeof(DWORD))) { hr = E_INVALIDARG; goto out_nomutex; } DISPLAYPARAMETERS( SET_RESOURCES_ID, lpResourceList, 0, 0, 0, 0); ACQMUTEX(g_hQoSMutex); if (!m_bQoSEnabled) // just return goto out; /* * Get configurable resource info */ pResource=lpResourceList->aResources; for (i=0; i < lpResourceList->cResources; i++) { TCHAR szKey[10]; // should be way enough for a resource ID int nUnits=INT_MAX; int nLeaveUnused=0; // build and open the key wsprintf(szKey, "%d", pResource[i].resourceID); RegEntry reQosResource(szKey, reQoSResourceRoot.GetKey(), FALSE, KEY_READ); // MaxUnits: // run through the list of resources and make sure none of the // resources was set to a number of units above the allowed maximum // if it was, trim and warn // get maximum numbers for the resource, if any, from the registry nUnits = reQosResource.GetNumberIniStyle(TEXT("MaxUnits"), INT_MAX); // is the client trying to set the resource to a higher value ? if (pResource[i].nUnits > nUnits) { pResource[i].nUnits = nUnits; hr = QOS_W_MAX_UNITS_EXCEEDED; } // LeaveUnused: // leave some of the resource unused, as configured // use different default value depending on the resource switch (pResource[i].resourceID) { case RESOURCE_OUTGOING_BANDWIDTH: nLeaveUnused = 30; break; default: nLeaveUnused = 10; break; } nLeaveUnused = reQosResource.GetNumberIniStyle( TEXT("LeaveUnused"), nLeaveUnused); pResource[i].nUnits = (pResource[i].nUnits * (100 - nLeaveUnused)) / 100; } /* * Add the resource to the list */ // run through the input resource list and store the resources // resource availability is NOT accumulative pResource=lpResourceList->aResources; for (i=0; i < lpResourceList->cResources; i++) { pResourceInt = m_pResourceList; pPrevResourcefLink = &m_pResourceList; fResourceFound = FALSE; while (pResourceInt != 0) { if (pResourceInt->resource.resourceID == pResource[i].resourceID) { // found a match // did the total number of units change for this resource ? if (pResourceInt->resource.nUnits != pResource[i].nUnits) { // update the now available units // since we could end up with less units than what was allocated // we are issuing a NotifyNow at the end of this call pResourceInt->nNowAvailUnits = pResource[i].nUnits - (pResourceInt->resource.nUnits - pResourceInt->nNowAvailUnits); if (pResourceInt->nNowAvailUnits < 0) pResourceInt->nNowAvailUnits = 0; } // override the previous setting RtlCopyMemory( &(pResourceInt->resource), &(pResource[i]), sizeof(RESOURCE)); fResourceFound = TRUE; break; } // not this one. try next one. pPrevResourcefLink = &(pResourceInt->fLink); pResourceInt = pResourceInt->fLink; } // while if (fResourceFound) continue; // not found. add the resource pResourceInt = (LPRESOURCEINT) MEMALLOC(sizeof(RESOURCEINT)); ASSERT(pResourceInt); if (!pResourceInt) { ERRORMSG(("IQoS::SetResources: MEMALLOC failed on RESOURCEINT\n")); hr = E_OUTOFMEMORY; goto out; } // copy the resource in RtlCopyMemory( &(pResourceInt->resource), &(pResource[i]), sizeof(RESOURCE)); pResourceInt->fLink = NULL; pResourceInt->nNowAvailUnits = pResourceInt->resource.nUnits; *pPrevResourcefLink = pResourceInt; // increment the number of resources we're tracking // this number will never go down m_cResources++; // next resource } // for // since there was a possible change in the resource availability, // run an immediate notification cycle if (SUCCEEDED(hr)) NotifyNow(); out: DISPLAYQOSOBJECT(); RELMUTEX(g_hQoSMutex); out_nomutex: DEBUGMSG(ZONE_IQOS,("IQoS::SetResources - leave, hr=0x%x\n", hr)); return hr; } /*************************************************************************** Name : CQoS::GetResources Purpose : Gets the list of resources available to the QoS module Parameters: lppResourceList - an address where QoS will place a pointer to a buffer with the list of resources available to QoS. The caller must use CQoS::FreeBuffer to free this buffer. Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::GetResources (LPRESOURCELIST *lppResourceList) { HRESULT hr = NOERROR; ULONG i; LPRESOURCELIST prl=NULL; LPRESOURCEINT pResourceInt=NULL; RESOURCE *pResource=NULL; DEBUGMSG(ZONE_IQOS,("IQoS::GetResources\n")); /* * parameter validation */ // lpResourceList should at least have a count DWORD if (!lppResourceList || IsBadWritePtr(lppResourceList, (UINT) sizeof(DWORD))) { hr = E_INVALIDARG; goto out_nomutex; } // no list yet *lppResourceList = NULL; if (!m_bQoSEnabled) // just return goto out_nomutex; ACQMUTEX(g_hQoSMutex); /* * Get resource info */ // allocate a buffer for the resources info prl = (LPRESOURCELIST) MEMALLOC(sizeof(RESOURCELIST) + ((LONG_PTR)m_cResources-1)*sizeof(RESOURCE)); if (!prl) { hr = E_OUTOFMEMORY; ERRORMSG(("GetResources: MEMALLOC failed\n")); goto out; } RtlZeroMemory((PVOID) prl, sizeof(RESOURCELIST) + ((LONG_PTR)m_cResources-1)*sizeof(RESOURCE)); // now fill in the information prl->cResources = m_cResources; pResourceInt=m_pResourceList; for (i=0; i < m_cResources; i++) { ASSERT(pResourceInt); // see if we have a NULL resource pointer // shouldn't happen, but we shouldn't crash if it does if (!pResourceInt) { hr = QOS_E_INTERNAL_ERROR; ERRORMSG(("GetResources: bad QoS internal resource list\n")); goto out; } // copy the resource info into the buffer RtlCopyMemory( &(prl->aResources[i]), &(pResourceInt->resource), sizeof(RESOURCE)); // next resource pResourceInt = pResourceInt->fLink; } // for *lppResourceList = prl; out: DISPLAYQOSOBJECT(); RELMUTEX(g_hQoSMutex); out_nomutex: DEBUGMSG(ZONE_IQOS,("IQoS::GetResources - leave, hr=0x%x\n", hr)); return hr; } /*************************************************************************** Name : CQoS::SetClients Purpose : Tells the QoS module what are the priorities of the requesting streams. This allows the QoS module to allocate resources appropriately. Parameters: lpClientList - list of clients and their respective priorities Returns : HRESULT Comment : client info will override an already existing info for this client ***************************************************************************/ HRESULT CQoS::SetClients(LPCLIENTLIST lpClientList) { HRESULT hr = NOERROR; ULONG i; BOOL fClientFound=FALSE; LPCLIENTINT pClientInt=NULL, *pPrevClientfLink=NULL, pClientNew=NULL;; LPCLIENT pClient=NULL; DEBUGMSG(ZONE_IQOS,("IQoS::SetClients\n")); /* * parameter validation */ // lpClientList should at least have a count DWORD if (!lpClientList || IsBadReadPtr(lpClientList, (UINT) sizeof(DWORD))) { hr = E_INVALIDARG; goto out_nomutex; } DISPLAYPARAMETERS( SET_CLIENTS_ID, lpClientList, 0, 0, 0, 0); ACQMUTEX(g_hQoSMutex); if (!m_bQoSEnabled) // just return goto out; // first remove existing clients that are being set again // this will make it easier to store clients in a priority order pClient=lpClientList->aClients; for (i=0; i < lpClientList->cClients; i++) { pClientInt = m_pClientList; pPrevClientfLink = &m_pClientList; fClientFound = FALSE; while (pClientInt != 0) { if (COMPARE_GUIDS( &(pClientInt->client.guidClientGUID), &(pClient[i].guidClientGUID))) { // found a match LPCLIENTINT pClientIntNext=pClientInt->fLink; // special case for internal calls from RequestResources // we want to preserve the original priority before freeing if (pClient[i].priority == QOS_LOWEST_PRIORITY) pClient[i].priority = pClientInt->client.priority; // free the requests for this client // NOTE: we're not going to recreate the request list from // the one in the resource list. it will be created on the // fly when needed. FreeListOfRequests(&(pClientInt->pRequestList)); // free the client record MEMFREE(pClientInt); *pPrevClientfLink = pClientIntNext; fClientFound = TRUE; break; } // not this one. try next one. pPrevClientfLink = &(pClientInt->fLink); pClientInt = pClientInt->fLink; } // while // next resource } // for // now store the clients in the input list in priority order pClient=lpClientList->aClients; for (i=0; i < lpClientList->cClients; i++) { pClientInt = m_pClientList; pPrevClientfLink = &m_pClientList; while (pClientInt != 0) { // as long as the priority of the new client is higher than or equal to the one // in the list, we continue to traverse the list if (pClient[i].priority < pClientInt->client.priority) { // this is the place to insert this client break; } // not time to insert yet. next client pPrevClientfLink = &(pClientInt->fLink); pClientInt = pClientInt->fLink; } // while // not found. add the client pClientNew = (LPCLIENTINT) MEMALLOC(sizeof(CLIENTINT)); ASSERT(pClientNew); if (!pClientNew) { ERRORMSG(("IQoS::SetClients: MEMALLOC failed on CLIENTINT\n")); hr = E_OUTOFMEMORY; goto out; } // copy the resource in RtlCopyMemory( &(pClientNew->client), &(pClient[i]), sizeof(CLIENT)); pClientNew->fLink = pClientInt; *pPrevClientfLink = pClientNew; // next resource } // for out: DISPLAYQOSOBJECT(); RELMUTEX(g_hQoSMutex); out_nomutex: DEBUGMSG(ZONE_IQOS,("IQoS::SetClients -leave, hr=0x%x\n", hr)); return hr; } /*************************************************************************** Name : CQoS::NotifyNow Purpose : Tells the QoS module to initiate a notification cycle as soon as possible. Parameters: None Returns : HRESULT Comment : Don't call from within a notify proc. ***************************************************************************/ HRESULT CQoS::NotifyNow(void) { HRESULT hr = NOERROR; DEBUGMSG(ZONE_IQOS,("IQoS::NotifyNow\n")); SetEvent(m_evImmediateNotify); DEBUGMSG(ZONE_IQOS,("IQoS::NotifyNow - leave, hr=0x%x\n", hr)); return hr; } /*************************************************************************** Name : CQoS::FreeBuffer Purpose : Frees a buffer allocated by the QoS module. Parameters: lpBuffer - a pointer to the buffer to free. This buffer must have been allocated by QoS Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::FreeBuffer(LPVOID lpBuffer) { HRESULT hr = NOERROR; DEBUGMSG(ZONE_IQOS,("IQoS::FreeBuffer\n")); if (lpBuffer) MEMFREE(lpBuffer); DEBUGMSG(ZONE_IQOS,("IQoS::FreeBuffer - leave, hr=0x%x\n", hr)); return hr; } /*************************************************************************** Name : CQoS::CQoS Purpose : The CQoS object constructor Parameters: none Returns : None Comment : ***************************************************************************/ inline CQoS::CQoS (void) { m_cRef = 0; // will be bumped to 1 by the explicit QI in CreateQoS m_pResourceList = NULL; m_cResources = 0; m_pClientList = NULL; m_evThreadExitSignal = NULL; m_evImmediateNotify = NULL; m_hThread = NULL; m_bQoSEnabled = TRUE; m_bInNotify = FALSE; m_nSkipHeartBeats = 0; m_hWnd = NULL; m_nLeaveForNextPri = 5; // can't use ++ because RISC processors may translate to several instructions InterlockedIncrement((long *) &g_cQoSObjects); } /*************************************************************************** Name : CQoS::~CQoS Purpose : The CQoS object destructor Parameters: none Returns : None Comment : ***************************************************************************/ inline CQoS::~CQoS (void) { // can't use ++ because RISC processors may translate to several instructions InterlockedDecrement((long *) &g_cQoSObjects); g_pQoS = (CQoS *)NULL; } /*************************************************************************** Name : CQoS::Initialize Purpose : Initializes the QoS object Parameters: None Returns : HRESULT Comment : ***************************************************************************/ HRESULT CQoS::Initialize(void) { HRESULT hr=NOERROR; OSVERSIONINFO tVersionInfo; /* * Initialize the object */ ACQMUTEX(g_hQoSMutex); // first see if QoS is enabled RegEntry reQoS(QOS_KEY, HKEY_LOCAL_MACHINE, FALSE, KEY_READ); m_bQoSEnabled = reQoS.GetNumberIniStyle(TEXT("Enable"), TRUE); if (!m_bQoSEnabled) { // don't create a thread, but return success DEBUGMSG(ZONE_IQOS,("Initialize: QoS not enabled\n")); hr = NOERROR; goto out; } /* * QoS notification thread */ // create an event that will be used to signal the thread to terminate // CreateEvent(No security attr's, no manual reset, not signalled, no name) m_evThreadExitSignal = CreateEvent(NULL, FALSE, FALSE, NULL); ASSERT(m_evThreadExitSignal); if (!(m_evThreadExitSignal)) { ERRORMSG(("Initialize: Exit event creation failed: %x\n", GetLastError())); hr = E_FAIL; goto out; } // create an event that will be used to signal the thread to initiate // an immediate notify cycle // CreateEvent(No security attr's, no manual reset, not signalled, no name) m_evImmediateNotify = CreateEvent(NULL, FALSE, FALSE, NULL); ASSERT(m_evImmediateNotify); if (!(m_evImmediateNotify)) { ERRORMSG(("Initialize: Immediate notify event creation failed: %x\n", GetLastError())); hr = E_FAIL; goto out; } //Set the OS flag tVersionInfo.dwOSVersionInfoSize=sizeof (OSVERSIONINFO); if (!(GetVersionEx (&tVersionInfo))) { ERRORMSG(("Initialize: Couldn't get version info: %x\n", GetLastError())); hr = E_FAIL; goto out; } if (tVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { bWin9x=TRUE; }else { if (tVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { bWin9x=FALSE; }else { //How on earth did we get here? ASSERT (0); hr=E_FAIL; goto out; } } out: RELMUTEX(g_hQoSMutex); return hr; } /*************************************************************************** Name : CreateQoS Purpose : Creates the QoS object and return an IQoS interface pointer Parameters: Returns : HRESULT Comment : CreateQoS will only create one instance of the QoS object. Additional calls will return the same interface pointer ***************************************************************************/ extern "C" HRESULT WINAPI CreateQoS ( IUnknown *punkOuter, REFIID riid, void **ppv) { CQoS *pQoS; HRESULT hr = NOERROR; *ppv = 0; if (punkOuter) return CLASS_E_NOAGGREGATION; /* * instantiate the QoS object */ ACQMUTEX(g_hQoSMutex); // only instantiate a new object if it doesn't already exist if (g_cQoSObjects == 0) { if (!(pQoS = new CQoS)) { hr = E_OUTOFMEMORY; goto out; } // Save pointer g_pQoS = pQoS; // initialize the QoS object hr = pQoS->Initialize(); } else { // this is the case when the object was already instantiaed in this // process, so we only want to return the object pointer. pQoS = g_pQoS; } // must have only one QoS object at this point ASSERT(g_cQoSObjects == 1); RELMUTEX(g_hQoSMutex); // get the IQoS interface for the caller if (pQoS) { // QueryInterface will get us the interface pointer and will AddRef // the object hr = pQoS->QueryInterface (riid, ppv); } else hr = E_FAIL; out: return hr; } /*************************************************************************** Name : QoSEntryPoint Purpose : Called by nac.dll (where the QoS lives these days) to make the necessary process attach and detach initializations Parameters: same as a standard DllEntryPoint Returns : ***************************************************************************/ extern "C" BOOL APIENTRY QoSEntryPoint( HINSTANCE hInstDLL, DWORD dwReason, LPVOID lpReserved) { BOOL fRet=TRUE; switch (dwReason) { case DLL_PROCESS_ATTACH: QOSDEBUGINIT(); // create a no-name mutex to control access to QoS object data if (!g_hQoSMutex) { g_hQoSMutex = CreateMutex(NULL, FALSE, NULL); ASSERT(g_hQoSMutex); if (!g_hQoSMutex) { ERRORMSG(("QoSEntryPoint: CreateMutex failed, 0x%x\n", GetLastError())); fRet = FALSE; } } break; case DLL_PROCESS_DETACH: if (g_hQoSMutex) CloseHandle(g_hQoSMutex); g_hQoSMutex = NULL; DBGDEINIT(&ghDbgZoneQoS); break; default: break; } return fRet; }