/* redistribute credits based on activity change */ static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, HTC_ENDPOINT_CREDIT_DIST *pEPDistList) { HTC_ENDPOINT_CREDIT_DIST *pCurEpDist = pEPDistList; /* walk through the list and remove credits from inactive endpoints */ while (pCurEpDist != NULL) { if (pCurEpDist->ServiceID != WMI_CONTROL_SVC) { if (!IS_EP_ACTIVE(pCurEpDist)) { if (pCurEpDist->TxQueueDepth == 0) { /* EP is inactive and there are no pending messages, reduce credits back to zero */ ReduceCredits(pCredInfo, pCurEpDist, 0); } else { /* we cannot zero the credits assigned to this EP, but to keep * the credits available for these leftover packets, reduce to * a minimum */ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsMin); } } } /* NOTE in the active case, we do not need to do anything further, * when an EP goes active and needs credits, HTC will call into * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK_CREDITS */ pCurEpDist = pCurEpDist->pNext; } }
/* redistribute credits based on activity change */ static void RedistributeCredits(struct common_credit_state_info *pCredInfo, struct htc_endpoint_credit_dist *pEPDistList) { struct htc_endpoint_credit_dist *pCurEpDist = pEPDistList; /* walk through the list and remove credits from inactive endpoints */ while (pCurEpDist != NULL) { #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS if ((pCurEpDist->ServiceID == WMI_DATA_BK_SVC) || (pCurEpDist->ServiceID == WMI_DATA_BE_SVC)) { /* force low priority streams to always be active to retain their minimum credit distribution */ SET_EP_ACTIVE(pCurEpDist); } #endif if (pCurEpDist->ServiceID != WMI_CONTROL_SVC) { if (!IS_EP_ACTIVE(pCurEpDist)) { if (pCurEpDist->TxQueueDepth == 0) { /* EP is inactive and there are no pending messages, reduce credits back to zero */ ReduceCredits(pCredInfo, pCurEpDist, 0); } else { /* we cannot zero the credits assigned to this EP, but to keep * the credits available for these leftover packets, reduce to * a minimum */ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsMin); } } } /* NOTE in the active case, we do not need to do anything further, * when an EP goes active and needs credits, HTC will call into * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK_CREDITS */ pCurEpDist = pCurEpDist->pNext; } }
/* HTC has an endpoint that needs credits, pEPDist is the endpoint in question */ static void SeekCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, HTC_ENDPOINT_CREDIT_DIST *pEPDist) { HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; int credits = 0; int need; do { if (pEPDist->ServiceID == WMI_CONTROL_SVC) { /* we never oversubscribe on the control service, this is not * a high performance path and the target never holds onto control * credits for too long */ break; } #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS if (pEPDist->ServiceID == WMI_DATA_VI_SVC) { if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) { /* limit VI service from oversubscribing */ break; } } if (pEPDist->ServiceID == WMI_DATA_VO_SVC) { if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) { /* limit VO service from oversubscribing */ break; } } #else if (pEPDist->ServiceID == WMI_DATA_VI_SVC) { if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) || (pCredInfo->CurrentFreeCredits <= pEPDist->TxCreditsPerMaxMsg)) { /* limit VI service from oversubscribing */ /* at least one free credit will not be used by VI */ break; } } if (pEPDist->ServiceID == WMI_DATA_VO_SVC) { if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) || (pCredInfo->CurrentFreeCredits <= pEPDist->TxCreditsPerMaxMsg)) { /* limit VO service from oversubscribing */ /* at least one free credit will not be used by VO */ break; } } #endif /* for all other services, we follow a simple algorithm of * 1. checking the free pool for credits * 2. checking lower priority endpoints for credits to take */ /* give what we can */ credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek); if (credits >= pEPDist->TxCreditsSeek) { /* we found some to fullfill the seek request */ break; } /* we don't have enough in the free pool, try taking away from lower priority services * * The rule for taking away credits: * 1. Only take from lower priority endpoints * 2. Only take what is allocated above the minimum (never starve an endpoint completely) * 3. Only take what you need. * * */ /* starting at the lowest priority */ pCurEpDist = pCredInfo->pLowestPriEpDist; /* work backwards until we hit the endpoint again */ while (pCurEpDist != pEPDist) { /* calculate how many we need so far */ need = pEPDist->TxCreditsSeek - pCredInfo->CurrentFreeCredits; if ((pCurEpDist->TxCreditsAssigned - need) >= pCurEpDist->TxCreditsMin) { /* the current one has been allocated more than it's minimum and it * has enough credits assigned above it's minimum to fullfill our need * try to take away just enough to fullfill our need */ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsAssigned - need); if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) { /* we have enough */ break; } } pCurEpDist = pCurEpDist->pPrev; } if ((pCredInfo->CurrentFreeCredits < pEPDist->TxCreditsSeek) && (pEPDist->TxCreditsAssigned < pEPDist->TxCreditsNorm)) { /* we don't have enough in the free pool, try taking away some unused credits from high priority services * * The rule for taking away credits: * 1. It's ok to take away from higher priority service as long as you take only part of what is unused. * 2. Only take what is allocated above the minimum (never starve an endpoint completely) * 3. Only take what you need. * * */ /* Go towards higher priority */ pCurEpDist = pEPDist->pPrev; /* work backwards until we hit the top priority */ while (pCurEpDist != NULL) { /* calculate how many we need so far */ need = pEPDist->TxCreditsSeek - pCredInfo->CurrentFreeCredits; /* try to take away half of what is not being used by the EP */ need = min(pCurEpDist->TxCredits/2, need); if ((need > 0) && ((pCurEpDist->TxCreditsAssigned - need) >= pCurEpDist->TxCreditsMin)) { /* the current one has been allocated more than it's minimum and it * has enough credits assigned above it's minimum to fullfill our need * try to take away just enough to fullfill our need */ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsAssigned - need); if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) { /* we have enough */ break; } } pCurEpDist = pCurEpDist->pPrev; } } /* return what we can get */ credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek); } while (FALSE); /* did we find some credits? */ if (credits) { /* give what we can */ GiveCredits(pCredInfo, pEPDist, credits); } }
/* default credit distribution callback * This callback is invoked whenever endpoints require credit distributions. * A lock is held while this function is invoked, this function shall NOT block. * The pEPDistList is a list of distribution structures in prioritized order as * defined by the call to the HTCSetCreditDistribution() api. * */ static void ar6000_credit_distribute(void *Context, HTC_ENDPOINT_CREDIT_DIST *pEPDistList, HTC_CREDIT_DIST_REASON Reason) { HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context; switch (Reason) { case HTC_CREDIT_DIST_SEND_COMPLETE : pCurEpDist = pEPDistList; /* we are given the start of the endpoint distribution list. * There may be one or more endpoints to service. * Run through the list and distribute credits */ while (pCurEpDist != NULL) { if (pCurEpDist->TxCreditsToDist > 0) { /* return the credits back to the endpoint */ pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist; /* always zero out when we are done */ pCurEpDist->TxCreditsToDist = 0; if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsAssigned) { /* reduce to the assigned limit, previous credit reductions * could have caused the limit to change */ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsAssigned); } if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsNorm) { /* oversubscribed endpoints need to reduce back to normal */ ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsNorm); } if (!IS_EP_ACTIVE(pCurEpDist)) { /* endpoint is inactive, now check for messages waiting for credits */ if (pCurEpDist->TxQueueDepth == 0) { /* EP is inactive and there are no pending messages, * reduce credits back to zero to recover credits */ ReduceCredits(pCredInfo, pCurEpDist, 0); } } } pCurEpDist = pCurEpDist->pNext; } break; case HTC_CREDIT_DIST_ACTIVITY_CHANGE : RedistributeCredits(pCredInfo,pEPDistList); break; case HTC_CREDIT_DIST_SEEK_CREDITS : SeekCredits(pCredInfo,pEPDistList); break; case HTC_DUMP_CREDIT_STATE : AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Credit Distribution, total : %d, free : %d\n", pCredInfo->TotalAvailableCredits, pCredInfo->CurrentFreeCredits)); break; default: break; } /* sanity checks done after each distribution action */ A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits); A_ASSERT(pCredInfo->CurrentFreeCredits >= 0); }